Open-source, milestone-based on-chain escrow for freelancers & Web3 projects.
Getting Started · Architecture · Workflow · Roadmap · Contributing
- Why OpenEscrow
- Core Features (MVP)
- Architecture
- Tech Stack
- Getting Started
- Environment Variables
- How It Works
- Telegram Linking
- Non-Goals (MVP)
- Roadmap
- Contributing
- Security
- License
- Acknowledgements
- Disclaimer
Freelance projects — especially in Web3 — frequently break down over:
| Problem | Impact |
|---|---|
| Vague scope and unclear acceptance criteria | Disputes over "done" vs. "not done" |
| Payment trust gap (upfront risk vs. delivery risk) | Freelancers ghost, clients don't pay |
| No structured dispute loop | Projects stall, relationships break |
OpenEscrow addresses this with:
- On-chain escrow — transparent, rule-based fund releases via smart contract
- Structured milestone loop — submit → approve / reject → revise → resubmit
- Telegram as a secondary interface — get notified and take action without leaving your chat app
- Create escrow deals with structured milestones — each with title, description, acceptance criteria, and amount
- Fund escrow in USDC or USDT — no native tokens, no other ERC-20s
- Freelancer submits milestone deliverables (summary + links)
- Client approves (triggers on-chain fund release) or rejects with structured reasons
- Auto-transition to revision state after rejection — freelancer revises and resubmits
- Audit trail — full timeline of every action per deal (who did what, when)
- Telegram bot — receive deal notifications, approve/reject from inline keyboards
- SIWE authentication — one-click wallet sign-in (Sign-In With Ethereum); SIWE fires automatically on wallet connect, JWT session persists 24 hours
graph TD
subgraph Interfaces
WEB[Web Dashboard\nNext.js 14]
BOT[Telegram Bot\nTelegraf]
end
subgraph Backend
API[API Server\nFastify + Drizzle]
INDEXER[Chain Indexer\neth_getLogs poll]
DB[(PostgreSQL)]
end
subgraph Chain
CONTRACT[OpenEscrow.sol\nSepolia]
end
WEB -->|SIWE auth + REST| API
BOT -->|REST via api-client| API
API --- DB
INDEXER -->|poll every 12s| CONTRACT
INDEXER --> DB
API -->|approve → release funds| CONTRACT
WEB -->|deposit via wagmi| CONTRACT
| Package | Description |
|---|---|
apps/api |
Fastify backend — SIWE auth, deal lifecycle, milestones, chain indexer |
apps/web |
Next.js 14 dashboard — wallet connect, client + freelancer flows |
apps/bot |
Telegraf Telegram bot — deal status, approve/reject, notification polling |
contracts |
OpenEscrow.sol + Hardhat tests + Sepolia deploy scripts |
packages/shared |
TypeScript types, constants, and contract ABIs shared across all apps |
| Layer | Technology |
|---|---|
| Smart Contracts | Solidity 0.8.24 · Hardhat · OpenZeppelin (ReentrancyGuard, SafeERC20, Ownable) |
| Backend API | TypeScript · Fastify · Drizzle ORM · PostgreSQL |
| Auth | SIWE (Sign-In With Ethereum) · JWT session tokens |
| Frontend | Next.js 14 (App Router) · React · Tailwind CSS · wagmi · viem · RainbowKit |
| Telegram Bot | TypeScript · Telegraf |
| Validation | Zod — every API input and env config validated at startup |
| Logging | pino — structured JSON logs throughout |
| Testing | Vitest (API + bot + web) · Hardhat (contracts) |
| Deployment | Docker · Docker Compose (single-server self-hosted) |
| Target Chain | Ethereum Sepolia testnet (mainnet only after audit) |
| Tool | Version |
|---|---|
| Node.js | 22+ |
| pnpm | 9+ (npm install -g pnpm) |
| Docker + Docker Compose | v2+ |
# 1. Clone the repository
git clone https://github.com/baties/OpenEscrow.git
cd OpenEscrow
# 2. Install dependencies
pnpm install
# 3. Copy and configure environment variables
cp .env.example .env
# Edit .env — fill in required values (see Environment Variables section)
# 4. Start all services
docker compose up -d
# 5. Verify everything is running
docker compose ps # all 4 services healthy
curl http://localhost:3001/api/v1/health # {"status":"ok","timestamp":"..."}
open http://localhost:3000 # Web dashboardServices start in dependency order: postgres → api → web + bot.
# Run all tests
pnpm test
# Lint all packages
pnpm lint
# Build all packages (TypeScript check)
pnpm build
# View logs from a running service
docker compose logs -f api
docker compose logs -f botA developer-only compose file adds pgAdmin for browsing the PostgreSQL database. Never deploy this to production.
# Start full stack + pgAdmin
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# Or start only postgres + pgAdmin (run API/Web/Bot locally via pnpm dev)
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d postgres pgadminpgAdmin opens at http://localhost:5050 — login: admin@openescrow.dev / admin.
The local PostgreSQL server is pre-configured — no manual setup needed.
# Compile contracts
pnpm --filter @open-escrow/contracts build
# Run contract tests (69 tests)
pnpm --filter @open-escrow/contracts test
# Deploy to Sepolia (requires DEPLOYER_PRIVATE_KEY in .env)
pnpm --filter @open-escrow/contracts deploy:sepolia
# Export ABI to packages/shared after deployment
pnpm --filter @open-escrow/contracts export-abiCopy .env.example and fill in the required values:
cp .env.example .envAll variables are validated at startup via Zod — the process exits immediately with a clear error message if a required value is missing or invalid.
Key variables that require real values
| Variable | How to get it |
|---|---|
JWT_SECRET |
openssl rand -hex 64 |
BOT_API_SECRET |
openssl rand -hex 32 |
POSTGRES_PASSWORD |
Any strong password |
TELEGRAM_BOT_TOKEN |
@BotFather on Telegram |
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID |
cloud.walletconnect.com |
RPC_URL |
Sepolia RPC from Alchemy / Infura / QuickNode |
CONTRACT_ADDRESS |
Output of pnpm --filter @open-escrow/contracts deploy:sepolia |
USDC_ADDRESS / USDT_ADDRESS |
Sepolia token addresses (examples in .env.example) |
graph TD
A[1. Client Creates Deal] --> B[2. Freelancer Reviews & Agrees]
B --> C[3. Client Funds Escrow on-chain]
C --> D[4. Freelancer Works on Milestone]
D --> E[5. Freelancer Submits Deliverables]
E --> F{6. Client Reviews}
F -->|Approve| G[Funds Released on-chain]
F -->|Reject + Reasons| H[Milestone → Revision State]
H --> D
G --> I{More Milestones?}
I -->|Yes| D
I -->|No| J[Deal Completed ✓]
| State | Description |
|---|---|
DRAFT |
Deal created by client, awaiting freelancer agreement |
AGREED |
Freelancer confirmed milestones and acceptance criteria |
FUNDED |
Client deposited funds to smart contract |
SUBMITTED |
Freelancer submitted milestone deliverables |
APPROVED |
Client approved — funds released on-chain |
REJECTED |
Client rejected with structured reasons |
REVISION |
Freelancer revising after rejection (auto-set) |
COMPLETED |
All milestones approved, deal finished (auto-set) |
CANCELLED |
Deal cancelled — unreleased funds returned to client |
Telegram is a secondary interface — the backend API remains the single source of truth. All Telegram actions go through the API, which enforces roles and state transitions.
- Start the bot and run
/link - The bot displays your Telegram user ID and instructions
- Log into the web dashboard and enter the one-time code generated there
- Your Telegram account is now linked — the bot will send deal notifications
/deals— list your active deals/status <dealId>— view deal details and milestone status- Inline keyboards — approve or reject milestones (client), confirm submissions (freelancer)
- Notifications — funded, submitted, approved, rejected, cancelled events pushed in real time (30s polling)
Unlink anytime from the web dashboard — revokes bot access immediately.
These are explicitly out of scope for the initial release:
- Arbitration, dispute councils, or voting mechanisms (no arbitration — only approve/reject/revise loop)
- Native token or non-stablecoin support (USDC and USDT only)
- Multi-chain deployment (Sepolia testnet only — mainnet after audit)
- Email or magic-link authentication (wallet sign-in only)
- AI-assisted milestone drafting or submission summaries (Phase 5, post-MVP)
| Phase | Milestone | Status |
|---|---|---|
| 0 | Repo Bootstrap — monorepo, Docker, CI, shared types | ✅ Done |
| 1 | Smart Contracts — escrow, milestones, cancel/refund, events | ✅ Done |
| 2 | Backend API — auth, deal lifecycle, chain indexer, Telegram linking | ✅ Done |
| 3 | Web Dashboard — wallet connect, client + freelancer flows, timeline | ✅ Done |
| 4 | Telegram Bot — commands, inline keyboards, notification polling | ✅ Done |
| 5 | AI Clarity Layer — milestone drafts, submission summaries, revision notes via OpenAI | ⏳ Post-MVP |
| 6 | Hardening — rate limits, circuit breakers, observability, mainnet deployment after audit | ⏳ Post-MVP |
Contributions are welcome and greatly appreciated.
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Commit your changes using Conventional Commits (
feat:,fix:,chore:, etc.) - Run
pnpm lint && pnpm test— both must pass - Push and open a Pull Request
| Area | Ideas |
|---|---|
| Smart Contracts | Gas optimizations, additional token support (post-MVP), formal verification |
| Multi-chain | Deployment templates for Polygon, Arbitrum, Base |
| Web Dashboard | UX improvements, mobile responsiveness, dark mode |
| Telegram Bot | Richer inline keyboards, media support |
| AI Layer | Prompt templates, evaluation harness, local LLM support (Phase 5) |
| Security | Audit findings, fuzzing, static analysis integrations |
| Docs | Tutorials, translations, self-hosting guides |
See CONTRIBUTING.md for the full guide.
The OpenEscrow smart contracts have not been audited. Do NOT use on mainnet with real funds.
- MVP targets Sepolia testnet only
- Contract mainnet deployment requires a professional security audit
- Never store private keys in code or commit them to the repository
- All secrets go in
.env(gitignored)
Please report security vulnerabilities responsibly — do not open a public GitHub issue:
- Use GitHub Security Advisories
- We will acknowledge within 48 hours and work with you on a coordinated fix
This project is licensed under the MIT License — see the LICENSE file for details.
- OpenZeppelin — battle-tested smart contract security libraries
- Hardhat — Solidity development and testing environment
- Fastify — fast and low-overhead Node.js web framework
- Drizzle ORM — TypeScript-first ORM with type-safe queries
- wagmi + viem — type-safe EVM hooks for React
- RainbowKit — wallet connection UI
- Telegraf — Telegram bot framework for Node.js
OpenEscrow is an open-source tool provided as-is. It does not provide financial, legal, or investment advice. Smart contracts may contain bugs. Use at your own risk. Always verify contract code and conduct your own security review before handling real funds.
Report a Bug · Request a Feature · Discussions
If you find OpenEscrow useful, please consider giving it a ⭐