TaskMarket
Campus services marketplace for international students — post a task, get bids, settle in USDC on-chain via a non-custodial escrow contract on Coinbase Base.
Context
International students on a single campus coordinate day-to-day help — package pickup, food runs, airport rides, tutoring, moving — almost entirely through WeChat groups. The friction is constant: tasks scroll past, prices are negotiated in DMs, there’s no trust signal, and there’s no payment guarantee. People show up to airports and don’t get paid; people pay deposits to someone who never delivers.
TaskMarket is a structured marketplace built around the actual hard part: money handling. Post a task with a clear price, optional USDC deposit into a non-custodial escrow contract on Coinbase Base, ranked bid book, realtime per-task chat, and auto-release on completion. The platform never holds user funds.
Approach
The economic core is a Solidity contract, not the backend
The product’s most distinctive piece is a single-file Solidity contract — TaskEscrow.sol on Coinbase Base — that holds USDC for in-flight tasks. It is the only path money can move.
Lifecycle:
- Poster
approve(USDC)thendeposit(taskId, payee, amount)from the browser via viem. - Poster picks a winning bid → order created → both sides chat in realtime.
- Taker submits completion evidence (text + up to 9 photos).
- Poster confirms → contract splits funds:
(amount - fee)to taker,feeto platform treasury. 5% fee, capped at 20% by constant. - Dispute path: either side opens a dispute → admin arbiter calls
resolveByOwner(taskId, favorPayee: bool)— a one-time binary on-chain call that routes funds to either the original payer or the assigned payee. There is no third destination address.
The contract has exactly four states (None / Funded / Released / Refunded) and no upgrade path. The arbiter key is the only on-chain privilege the operator holds — it can resolve disputes but cannot move funds anywhere else, change fee beyond cap, or re-fund a closed task.
Bid book + value score, not first-taker-wins
Each task accumulates a bid book where bids carry price, ETA, and a short pitch. A server-side valueScore combines price, taker rating, and completion history so the poster gets a sortable ranking instead of having to manually compare ten variations of “我能做 $5.”
Realtime coordination via Socket.IO
Three live surfaces:
- Bid heat on the market list — new bids land without refresh
- Online counter on the market header
- Per-task chat rooms when a bid matches, with pin / mute / archive / block / report
Background workers — the “what happens when no one clicks”
Four workers handle the actual operability of a non-trivial marketplace:
| Worker | What it does |
|---|---|
escrowReleaser | Auto-releases escrow when both sides confirm but the poster forgets to click release |
gasMonitor | Watches the arbiter wallet’s ETH balance — warns before disputes can’t be resolved |
orderTimeouts | Auto-cancels matched orders the taker never starts |
taskLifecycle | Sweeps stale tasks back to OPEN or expires them |
Pinyin-aware fuzzy search
A Chinese-language marketplace running on a Latin keyboard needs daiquhongbao → 代取红包 to work. The fuzzy search uses pinyin-pro so users don’t need a Chinese input method just to find a listing.
What Makes It Different
| Most “campus task” apps | TaskMarket |
|---|---|
| Fixed-price postings | Bid book (price + ETA + pitch) ranked by value score |
| Trust = vibes | Star ratings + repeat history + completion record |
| Pay via Venmo / e-Transfer | USDC on Base, locked in non-custodial contract |
| Platform holds your money | Platform cannot move user funds; only arbiter on disputes |
| Chinese names → bad search | Pinyin fuzzy search out of the box |
| Group-chat threads die | Per-task Socket.IO chat with pin / mute / archive / block |
Results & What I Learned
Active build, on Base testnet. Mainnet launch pending audit of TaskEscrow.sol. The repository model is split: public overview (taskmarket-overview) for screenshots / architecture / contract logic, and a private source repository for the React app, Express API, Prisma schema, and Hardhat deploy scripts.
The two non-obvious lessons:
Money handling is the product. A marketplace where settlement is “trust each other on Venmo” looks identical to a marketplace where settlement is “non-custodial escrow on Base” — until something goes wrong. The on-chain escrow doesn’t make it a better forum-style app; it makes the marketplace actually work at the moment the trade resolves. That’s the entire reason to build it on rails.
The workers are not afterthoughts. When I scoped this project I treated escrowReleaser, orderTimeouts, etc. as “polish to add later.” That was backwards. A marketplace with no auto-release and no timeout sweeper accumulates dead orders within a week and looks broken to every new user who lands on it. The workers are part of the load-bearing product, not infrastructure plumbing.
Tech Stack & Links
Frontend: React 19 · Vite 7 · TypeScript · Tailwind 4 · react-router-dom 7 · viem (browser wallet) · socket.io-client · lucide-react
Backend: Node.js 22 · Express · Socket.IO 4 · Prisma 6 (13 models) · JWT + bcryptjs · multer (uploads) · pinyin-pro (fuzzy search) · ethers v6 (chain ops)
Chain: Solidity 0.8.20 · Hardhat 2.22 · TaskEscrow.sol on Coinbase Base / Base Sepolia
Deploy: systemd + nginx, single VPS, run-production.sh lifecycle.
Repo: github.com/YSKM523/taskmarket-overview (private implementation kept separate)