Back to projects YSKM523 / taskmarket-overview

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.

Active Build · USDC Escrow on Base Founder · Core Developer last updated: May 16, 2026

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.

Landing — onboarding + value prop. USDC escrow opt-in surfaced from the first scroll.
Landing — onboarding + value prop. USDC escrow opt-in surfaced from the first scroll.
Market — ranked feed with live bid heat, category chips, fuzzy pinyin search, online counter.
Market — ranked feed with live bid heat, category chips, fuzzy pinyin search, online counter.
Task detail — bid book, poster profile, realtime activity timeline.
Task detail — bid book, poster profile, realtime activity timeline.

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:

  1. Poster approve(USDC) then deposit(taskId, payee, amount) from the browser via viem.
  2. Poster picks a winning bid → order created → both sides chat in realtime.
  3. Taker submits completion evidence (text + up to 9 photos).
  4. Poster confirms → contract splits funds: (amount - fee) to taker, fee to platform treasury. 5% fee, capped at 20% by constant.
  5. 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:

WorkerWhat it does
escrowReleaserAuto-releases escrow when both sides confirm but the poster forgets to click release
gasMonitorWatches the arbiter wallet’s ETH balance — warns before disputes can’t be resolved
orderTimeoutsAuto-cancels matched orders the taker never starts
taskLifecycleSweeps stale tasks back to OPEN or expires them

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.

Post a task — three-step form. The USDC on-chain escrow opt-in is explained inline, not hidden in a settings page.
Post a task — three-step form. The USDC on-chain escrow opt-in is explained inline, not hidden in a settings page.
Orders — full lifecycle (MATCHED → WAIT OWNER → DONE), inline evidence submission, dispute trigger.
Orders — full lifecycle (MATCHED → WAIT OWNER → DONE), inline evidence submission, dispute trigger.
Admin disputes — live arbiter wallet, gas health, chain ID, queue of cases. Operator sees the on-chain state before disputes start failing.
Admin disputes — live arbiter wallet, gas health, chain ID, queue of cases. Operator sees the on-chain state before disputes start failing.
Messages — realtime Socket.IO conversations scoped per task, with pin / archive / block.
Messages — realtime Socket.IO conversations scoped per task, with pin / archive / block.
Mobile — responsive throughout.
Mobile — responsive throughout.

What Makes It Different

Most “campus task” appsTaskMarket
Fixed-price postingsBid book (price + ETA + pitch) ranked by value score
Trust = vibesStar ratings + repeat history + completion record
Pay via Venmo / e-TransferUSDC on Base, locked in non-custodial contract
Platform holds your moneyPlatform cannot move user funds; only arbiter on disputes
Chinese names → bad searchPinyin fuzzy search out of the box
Group-chat threads diePer-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.

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)