VeriFund: A Charity Transparency Vault
๐ What Inspired Us
Our project was inspired by the hackathon's philanthropy theme and a simple, core question: "When I donate, where does my money actually go?"
We're all familiar with the "black box" of charitable giving. A donor gives $100, that money disappears, and at the end of the year, the charity releases a Form 990 showing it spent $5 million on "program services." This system requires total trust. It's a high-level, yearly summary, not a real-time, transaction-level ledger. It's too easy to hide fraud, shell companies, and conflicts of interest inside that one "program services" line item.
We were inspired to build a system that forces real-time, public accountability. Our goal isn't to prevent fraudโthat's a complex legal and real-world problem. Our goal is to deter fraud by making it public, permanent, and easily auditable by anyone.
๐ ๏ธ How We Built It
We built a "Web 2.5" decentralized application (dApp) that acts as a public escrow vault. The entire system is designed to be 100% transparent for the public while feeling like a simple "Web 2" app for the donor.
The Vault (Smart Contract): We wrote a Solidity smart contract and deployed it to the Sepolia testnet. This contract is the "vault."
The Reimbursement Model: Donors send funds directly to the contract. The charity cannot touch this money. To get paid, the charity must first pay for a real-world expense (e.g., water filters) with its own bank account.
Then, the charity requests a reimbursement from the vault by calling a function (requestReimbursement) and providing a public memo (e.g., "Invoice #45A - 100 water filters").
This action is logged as an immutable, public event, creating a permanent, real-time ledger of all spending.
The Donor Experience (The "Web 2.5" Flow): We knew we couldn't ask donors to have MetaMask.
Email Login: We used the Coinbase Embedded Wallet SDK. Donors sign in with just an email, and a secure "Smart Account" is automatically created for them.
The Donation: The user then donates from their Smart Account to our contract. The donate() function logs their unique wallet address, allowing us to track their donation.
The Public Experience (The Ledger):
A React + TypeScript frontend (built with Vite) that connects to the Sepolia testnet with a read-only provider.
It listens for both the DonationReceived and ReimbursementPaid events from our contract and displays them in a single, real-time list for all to see.
The Admin Experience (The Charity Portal):
A separate, secure admin page for the charity.
This page is gated by MetaMask. It checks if the connected wallet is the same one that deployed the contract (the charityAddress).
If they are the admin, they see a simple form to call the requestReimbursement function and submit their expense memo.
** ๐ง What We Learned The Oracle Problem is Real: Our initial idea was to verify that vendors were legitimate and that work was actually completed. We quickly learned that verifying real-world actions on a blockchain (the "Oracle Problem") is the single hardest part of any "blockchain for good" project and was impossible for a hackathon.
Pivot to Deterrence, Not Prevention: This led to our main pivot. We learned that a feasible system that solves part of the problem (public deterrence) is more valuable than a perfect system that can't be built. We're not preventing lies; we're just giving liars a permanent, public billboard to post on.
The Future of Web3 UX is "No Web3": Forcing users to install MetaMask and buy crypto on an exchange is a non-starter. We learned how powerful the Coinbase Embedded Wallet stack is. By abstracting away the blockchain, we created a user flow (email login -> credit card -> donate) that feels as simple as any normal web app.
๐โโ๏ธ Challenges We Faced
Architecture Hell (v1 vs. v2): Our biggest blocker was a broken onramp. We were trying to use the old v1 Coinbase Onramp API (which required a complex, custom backend) with the new v2 Embedded Wallet SDK. This created a cascade of errors. The fix was to delete all of our backend onramp code and replace our entire FundWallet component with the single, all-in-one component from the v2 SDK, which handled everything. We later found out that this v2 OnRamp required a lengthy application process which we could get in the future, but would be impossible in 12 hours on a Saturday.
Dependency Conflicts: We spent time debugging why our npm install was failing. It turned out the Coinbase SDKs require React 18, but our project was on the brand-new React 19. We had to downgrade our project to [email protected] to achieve compatibility.
Splitting the Wallets: We had to design an app that could handle two different wallet systems at once: the donor's invisible Embedded Wallet (managed by Coinbase's hooks) and the charity's MetaMask wallet (managed by a standard ethers.BrowserProvider). This required careful separation, especially in our AdminWrapper component, to ensure the admin was only using MetaMask.
Built With
- blockchain
- coinbase
- express.js
- node.js
- react.js
- resend
- smart-contract
- solidity
- typescript
- vercel
- vite
- web3
Log in or sign up for Devpost to join the conversation.