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
MilestoneEscrowfor 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()andgetActivity()
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
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
6
Shared progress dashboard
Reads don’t require a specific party’s signature — the platform’s own adapter is fine here:A minimal React timeline built on that endpoint:
src/routes/dashboard.ts
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 asfund/markDeliveredabove, 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.