For reviewers

One contract. Five states.

Everything HodL does is a call on one Soroban contract. Status only moves forward, and every function checks it first, which is what stops a release or refund from happening twice.

The state machine

CreatedAgreed, nothing locked
FundedUSDC in the contract
ReleasedPaid to the recipient
RefundedSent back by the recipient
ExpiredBack to the payer after the deadline
  • Created → Funded · fund · payer
  • Funded → Released · release · payer
  • Funded → Refunded · refund · recipient
  • Funded → Expired · claim_expired · payer
  • Created → Expired · claim_expired, nothing to return

A Funded escrow past its deadline stays stored as Funded until someone acts; reads report it as Expired, so no cron job is needed. The payer can still release, or the recipient refund, until it is claimed.

Escrows created
Code version
Network
USDC allowed
Upgrades
Admin

No admin function can move, freeze or redirect funds. While upgrades are enabled the admin can replace the code, UUPS-style; every upgrade is public on-chain, and lock_upgrades makes the code final.

Functions

FunctionSignerAllowed whenEffect
create_and_fund(...)PayerAmount > 0, not to yourself, deadline in the futureCreates and locks in one signature
create(...)PayerSame checksSaves the terms, locks nothing
fund(id)PayerNot funded, before the deadlineUSDC: payer → contract
release(id)PayerFunded, any timeUSDC: contract → recipient
refund(id)RecipientFunded, any timeUSDC: contract → payer
claim_expired(id)PayerNot settled, at or after the deadlineLocked USDC back to the payer
get_escrow(id)AnyoneAlwaysThe escrow plus its effective status
upgrade(hash) / lock_upgrades()AdminUntil upgrades are lockedSwaps the code; address and funds stay

Public API

Read-only, keyless and CORS-open JSON, cached for a few seconds. The app itself reads the chain directly, so it works without these.

  • GET /api/escrow/:idOne escrow and its phase
  • GET /api/escrow/:id/eventsIts receipts with transaction hashes
  • GET /api/escrows?ids=1,2,3Batch read
  • GET /api/users/:address/escrowsPaying and receiving ids; ?expand=1, ?activity=1
  • GET /api/statsContract id, escrow count, version, upgrade lock
  • GET /api/healthSoroban RPC and Horizon status
  • POST /api/friendbotFunds a Testnet account with XLM

Verify it yourself

Every escrow event carries topics ("hodl", action, id), so any escrow's history is one RPC getEvents filter.