Skip to main content
Someone working abroad wants to fund a house being built for family back home, but wiring the full contract value up front to a contractor they can’t personally supervise is a real risk — money can vanish long before the roof goes on. MilestoneEscrow splits payment into tranches tied to physical progress: the buyer funds one milestone at a time, the contractor delivers, the buyer confirms, and only then does that tranche release. This tutorial builds a full escrow-driven product around that flow, independent of any of the six RWA asset templates — MilestoneEscrow is a standalone stablecoin-denominated primitive in its own right.

What you’ll build

  • A backend that deploys a MilestoneEscrow for a construction schedule, with a platform-vetted local arbiter
  • A buyer-facing flow to fund milestones and approve or dispute deliveries
  • A contractor-facing flow to mark milestones delivered and claim a timelock release if the buyer goes unresponsive
  • A shared progress dashboard using getAllMilestones() and getActivity()

Prerequisites

  • Node.js 18+
  • A Stellar testnet account and secret key (S...) for the platform admin, plus test accounts for the buyer, contractor, and arbiter roles, all funded via the Stellar Friendbot
  • npm install @ankarachain/sdk express
This tutorial funds milestones with the Stellar Asset Contract for XLM (CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC), the same testnet-only demo token used in the Stellar Quickstart. XLM is volatile — deploy against a real stablecoin whitelisted on your EscrowFactory before handling real construction payments.

1

Scaffold the project

src/config.ts
2

Deploy the escrow for a construction schedule

The platform deploys on the buyer’s behalf once they’ve agreed a milestone schedule with the contractor, and assigns a platform-vetted local professional (e.g. a licensed quantity surveyor) as arbiter:
src/routes/deploy.ts
3

Buyer: fund and approve milestones

Funding, approval, disputes, and cancellation votes are all payer-signed actions — build the EscrowManager from the buyer’s own signer, not the platform’s:
src/routes/buyer.ts
On Stellar, funding a milestone doesn’t need a separate token-approval step — the transfer authorization happens inside the same Soroban invocation as fund(). That’s a difference from the EVM path, which needs an approve() call against the token contract first — see Escrow: Fund a milestone.
4

Contractor: mark delivery and fall back to the timelock

src/routes/contractor.ts
5

Arbiter: resolve a dispute

src/routes/arbiter.ts
If no arbiter was set at deployment (or the arbiter is unresponsive), a disputed milestone has no arbitration path — the only way forward is voteCancel() from both the payer and the payee, which refunds any remaining locked balance to the buyer. Always set an arbiter for real construction deals.
6

Shared progress dashboard

Reads don’t require a specific party’s signature — the platform’s own adapter is fine here:
src/routes/dashboard.ts
A minimal React timeline built on that endpoint:
src/components/DealTimeline.tsx

Going further

  • Cancellation: either party can call voteCancel() — the deal only cancels once both have voted, refunding any unfunded/remaining balance to the buyer. Wire this up the same way as fund/markDelivered above, from each party’s own signer.
  • Applying this to a tokenized asset instead of a service deal: the Invoice Financing Marketplace tutorial shows a simpler, single-payment alternative for receivables — use escrow instead when you need multiple tranches tied to verifiable delivery.

Next steps

EscrowManager

Full reference for every lifecycle method, including setArbiter and pause/unpause.

Escrow Guide

The EVM-side walkthrough, including the ERC-20 approve() step this Stellar tutorial skips.

CLI Commands

ankara deploy-escrow and the full set of ankara escrow-* commands.

Contracts: Escrow

The on-chain MilestoneEscrow architecture and dispute state machine.