Case study · FundChain · Product & Tech Lead · University project

Proving a donation was not changed, with a team of 15

FundChain is a fundraising platform for BINUS students, built as a university project. I was the Product & Tech Lead: I set the product scope, wrote the spec, built the foundation, and ran four workstreams on top of it.

15
People led
4
Workstreams
6
Sprints
99
Jira tasks
27
Planning docs
19
Spec conflicts resolved
01 · Problem

A donation record anyone with database access can edit

Student fundraisers ask people to trust a spreadsheet. If someone with database access edits a donation after it is paid, nobody outside can tell.

The brief was to make that edit visible. Keeping the money on-chain was out of scope: payments stay in rupiah through a normal gateway, and the chain is only a proof layer.

02 · Constraints

What we had to work with

Team
15 students across backend, blockchain, frontend and QA, most new to Solidity
Time
Six sprints, with a demo day at the end
Spec
27 planning docs written in parallel, with 19 places where they disagreed
Budget
Free tiers only: Vercel, Supabase, and the Sepolia testnet
Privacy
Anything written on a public chain stays there, so no names or emails
03 · Decisions

Four decisions, and what each one cost

  1. 01

    One canonical hash, defined in one place

    Each donation marked paid becomes the string v1|donationId|pseudonym|amount|paidAtSeconds, hashed with Keccak-256. It lives in a shared package used by both the notarizer and the checker, so the two can never format it differently.

    Trade-off. Fields are strict: whole rupiah only, a pseudonym like STU-00042 instead of a name, and the stored payment time instead of now(). The v1 prefix leaves room to change the format later.

  2. 02

    A relayer that can only write once

    The contract has two roles. A cold admin wallet can revoke the relayer; the hot relayer on the backend is the only one allowed to notarize, and a second write to the same donation reverts.

    Trade-off. A wrong hash can never be corrected on-chain. That is the point, but it means the hash format had to be settled before the first notarization.

  3. 03

    An outbox table instead of Redis and BullMQ

    The plan called for Redis and BullMQ. Notarization jobs go into a blockchain_transactions table instead, and a worker polls it with retry, backoff, and a FAILED state.

    Trade-off. Polling is slower than a queue, and on Vercel the worker only runs when a request or the daily cron wakes it. One less service to host for free and to explain to 15 people.

  4. 04

    Freeze the campaign, not just flag the row

    The integrity checker recomputes every hash from the database and compares it with the chain. A mismatch marks the donation TAMPERED, freezes the campaign, writes an audit log, and blocks every disbursement.

    Trade-off. One bad row stops a whole campaign until an admin looks at it. For a fundraiser, a false stop is cheaper than a quiet payout.

04 · Leading the team

From 27 docs to four workstreams

  • Resolved the 19 spec conflicts in writing before any code: hash format, relayer role, webhook idempotency, and the rest.
  • Built the monorepo myself (NestJS API, React web, Hardhat contract, Supabase) and had it live on Vercel and Sepolia by day two, so the team started from working code.
  • Opened five epics in Jira: 91 tasks across four workstreams, each with one owner, and 8 integration tasks (CI, sprint checks, demo rehearsal) that I kept.
  • Linked GitHub to Jira with BFD-key branch and PR names, and reviewed and merged the team's pull requests.
Backend31 tasks
Blockchain23 tasks
Frontend & UI17 tasks
QA20 tasks
Tech Lead & integration8 tasks
05 · Result

What runs today

  • Web and API live on Vercel; donations notarized on Sepolia with an Etherscan link per donation
  • A public proof page per donation that recomputes the hash in the browser
  • The demo path runs end to end: pay, hash, notarize, edit the amount in the database, detect, freeze, refuse the payout
  • Tests on the canonical hash, the contract (9 tests), the campaign state machine, the disbursement rules, and webhook signatures

Screenshots

FundChain · 1 of 4
Donation proof page: canonical payload, stored hash, transaction hash, and the on-chain hash on Sepolia
Donation proof page: canonical payload, stored hash, transaction hash, and the on-chain hash on Sepolia.
06 · Still open

What is not done yet

  • Sign-in is a persona switcher for the demo. Microsoft SSO for BINUS is researched but not built.
  • Payments run on a mock provider, with a Pakasir adapter ready. No real money has gone through it.
  • On Vercel the automatic check runs once a day, so a tamper can sit unnoticed for hours unless an admin runs the check.
  • It runs on Sepolia, a test network. Mainnet would need a funded relayer and a plan for gas.
Contact

Hiring for 2027? Let's talk.

I'm available full-time for 12 months in 2027 through the BINUS Enrichment Program. Email me with the role and team, and I'll send anything else you need. Freelance projects are welcome too.