Explore the code on DeepWiki

deepwiki.com/AlanRoybal/mhacks2026 →

A searchable wiki of the whole codebase: the escrow state machine, Stripe and USDC settlement, the ledger and reconciliation. Ask it anything about how Bounty works.

Source: github.com/AlanRoybal/mhacks2026

Bounty is escrow and settlement for everyday jobs

Small jobs between strangers, like mowing a lawn, moving a couch or a 30-minute tutoring session, usually get paid over Venmo, Zelle or cash. Nothing protects either side. If the poster pays first, the worker can disappear. If the worker goes first, they might never get paid. Payment apps move money, but they have no idea whether the work actually happened.

Bounty locks the money before the job is listed. It releases the money only when the agreed proof checks out and the review window closes without a dispute. Take away the escrow and there's no product left: nobody trusts a stranger with $15 on a job board.

How the money moves

Escrow lifecycle

  1. Terms, then money. The poster describes the job. Bounty drafts a proof checklist (for example, before and after photos plus a check-in at the address), and the poster edits and agrees to it before paying. That checklist works as the contract.
  2. Funded before listed. The poster pays the job price plus a 10% fee. A $15 job costs $16.50. The job only goes live once the payment is confirmed by a signed Stripe webhook (or once the USDC deposit lands on-chain).
  3. Verified release. The worker submits proof. AI grades it against the checklist, then the poster gets a review window (24 h, or 2 min in demo mode). If the poster doesn't dispute, the money releases on its own.
  4. Disputes and refunds are built in. A poster disputes a specific checklist item and explains what's wrong. An admin resolves it, but never on their own job. If nobody acts within 72 h, the AI's grade stands. If no worker finishes by the deadline, the poster gets a full refund.

Money flow

Two payment rails, one set of rules

Card / Apple Pay USDC
Where escrow sits Our Stripe platform balance (Stripe Connect separate charges and transfers) BountyEscrow smart contract on Base Sepolia
Payout Connect transfer to the worker, tied to the original charge Contract release to the worker's wallet
Fee 10% from the poster; the worker keeps the full job price None
Who can move funds Only payout and refund effects triggered by the state machine Our backend key can only pay the worker or refund the poster; after the deadline the poster can refund themselves

What makes it fintech and not just checkout

Safeguards

  • Append-only ledger. Every state change writes its ledger entry in the same transaction as the job update. Payouts and refunds run off that ledger (a transactional outbox), never directly from the app.
  • No double payouts. Every Stripe call has an idempotency key. Payouts check for an existing transfer before sending one. Webhooks are signature-verified and processed once each. A one-minute sweeper retries anything stuck, and failures go to a dead-letter queue.
  • Reconciliation. If a webhook never arrives, we ask Stripe directly. Money that shows up for a job that can't take it is refunded automatically. An audit endpoint checks each job's ledger against Stripe and the on-chain escrow state.
  • Fraud controls on the proof. Photos have to show a one-time code issued at check-in, fall inside the job's time window and be taken near the job location. The same photo can't count as both "before" and "after". Accepting a job requires Face ID, and posters can't take their own jobs.
  • Time on site, verified live. Every in-progress job is a live session in SpacetimeDB. While the job is open, the worker's phone reports its location about every 30 seconds, and SpacetimeDB keeps one on-site clock and geofence per job, pausing the clock if the worker leaves or the signal drops. In-person proof only pays out automatically if the worker was on site for long enough (40% of the job's estimate, up to 30 minutes). Otherwise the poster decides.
  • Unit economics. Jobs start at $5. Below that, card processing fees, which Stripe keeps even when a payment is refunded, would cost more than our 10% fee.

Inspiration

Our friends do odd jobs for neighbors, and people we know have been burned on both sides: a Venmo request that never got paid, or a deposit sent to someone who never showed up. Platforms like TaskRabbit solve this for professionals, but the informal economy still runs on trust and cash. We wanted the safety of escrow at the size of a $15 job.

What it does

  • Post a job with a price, location and deadline. Bounty drafts the proof checklist, and you fund it by card, Apple Pay or USDC.
  • Workers get matched, not spammed. Each worker has an AI "twin" built from their LinkedIn, Gmail and calendar, so it knows their skills and when they're free. The twin picks the best available worker and sends them a push offer they can accept from the lock screen with Face ID.
  • Both people can watch the job live. Once the worker taps Start, a Live Activity on the Lock Screen and in the Dynamic Island shows the on-site timer, proof progress (for example, 0/2), review and payment, for the worker and for the poster. The backend pushes updates straight to the activity, so it stays current even when the app is closed.
  • Proof, review and payout. The worker submits photos with the one-time code. AI checks them against the checklist and the poster reviews them. Then the money releases, and the worker watches it move from "in escrow" to "paying out" to "paid" on the Earnings screen, with the Stripe reference or transaction hash and a full history.

How we built it

Tech stack

  • iOS: native SwiftUI (iOS 17, Swift 6), Stripe PaymentSheet and Apple Pay, the Coinbase Wallet SDK for USDC checkout, actionable push notifications through APNs, Live Activities on the Lock Screen and in the Dynamic Island (ActivityKit, with a widget extension), background location while a job is open, Face ID with LocalAuthentication, EventKit, and the camera with location capture.
  • Backend: TypeScript and Hono on AWS Lambda behind API Gateway. DynamoDB holds jobs and the append-only ledger, and a DynamoDB stream drives the payout, refund and notification workers. EventBridge Scheduler runs the timers, plus S3 for proof photos and SQS as the dead-letter queue. Everything is defined in AWS CDK.
  • Live sessions: a SpacetimeDB module (TypeScript, on Maincloud) holds each in-progress job's session. Its reducers handle location pings, the on-site clock, leaving the site, signal loss, proof progress and the job's live phase, and a scheduled reducer runs every 30 seconds to notice phones that stopped reporting. Only the backend's identity can write. The backend reads sessions back to verify time on site and sends ActivityKit push updates to both people's Live Activities through APNs.
  • Job state machine: a pure, explicit state machine where every transition checks who's acting and when. A test runs 10,000 random sequences of events through it and checks that money never moves more than once.
  • Payments: Stripe Connect (separate charges and transfers, Accounts v2 connected accounts with Stripe-hosted onboarding, webhooks). A separate Node payments server with a SQLite ledger handles USDC settlement, reconciliation and the audit endpoint.
  • Smart contract: BountyEscrow.sol in Solidity with Foundry, deployed on Base Sepolia with Circle test USDC. It has a reentrancy guard, exact-amount deposits, and release or refund only.
  • AI: Claude on Amazon Bedrock drafts the checklists, grades proof and runs the twin. Amazon Titan embeddings handle worker matching.

Challenges we ran into

  • Getting money to move exactly once. Retries, slow webhooks and timers firing at the same time can all trigger a second payout. We made every money effect idempotent and checked against the ledger. We also stop blind retries once Stripe's 24-hour idempotency window has passed.
  • Custody. We had to decide who holds the money at each step. With cards, it's our Stripe platform balance. With USDC, it's a contract we can't take money out of for ourselves.
  • Letting AI near money safely. The AI grade alone never pays anyone. It only opens a review window that the poster controls, and every waiting state has a timeout with a safe default.
  • Proof that's hard to fake without dedicated hardware, using one-time codes, geofencing, time windows and a live on-site clock.
  • Live tracking without creeping anyone out. Location is only read while a job is open and stops when the proof is submitted. The poster sees distance and time on site, never coordinates.

Accomplishments that we're proud of

  • A complete card escrow loop in Stripe test mode: charge, hold, transfer to a connected account, and refund.
  • A working USDC escrow contract on Base Sepolia that the platform can't take funds from.
  • A dispute system with item-level disputes, admin resolution and timeouts, instead of a single "approve" button.
  • A native iOS app that feels like a consumer product, not a hackathon prototype.

What we learned

The payment button is the easy part. The hard parts are the questions around it: who holds the money, what happens when two events race, how you prove the work happened, and what you do when nobody responds. Answering those is what turned a job board into a fintech product.

What's next for Bounty

  • Chargeback handling: listen for Stripe dispute events, freeze the job, and send the proof photos, checklist and timeline to Stripe as evidence automatically.
  • Risk limits: escrow caps for new accounts, limits on how fast new jobs can be posted, and longer payout holds for low-reliability workers.
  • A poster-side escrow receipt and a treasury dashboard for reconciliation.
  • Milestone payments for bigger jobs, instant cash-out for a small fee, and an audited mainnet contract.

Built With

Share this project:

Updates

Submission history