💡 Inspiration
You donate to a flood relief campaign. Where does that money actually go? You don't know, and neither does anyone checking on your behalf. MACC is currently investigating Malaysian NGOs over RM230 million in misappropriated donations. JPPM has logged 314 fund-misuse complaints in five years. I kept asking myself how you'd actually fix that, and the answer came from somewhere completely unrelated to charity: a fish ladder, the staircase of pools river engineers use to help fish get past a dam, gate by gate, in plain view of anyone watching. I wanted every donation to move the same way.
🪜 What it does
Here's the proof that actually matters: connect a wallet that isn't the one registered to approve a campaign's spending, and the approval transaction rejects itself, on a public blockchain, not because of a rule in the app's interface, but because the smart contract itself refuses it. That's the core of CradleChain: nobody, including me, can quietly edit what actually happened to a donation.
- A donor reads a short What / Why / Who / Where / How breakdown of a campaign before giving anything. Giving needs no crypto wallet already funded with gas, a first donation triggers a small automatic test-token drip so a brand-new wallet can still send.
- When an NGO spends part of the donated funds, they log that step on-chain with a photo as proof, and only the one wallet address registered as that campaign's confirmer can approve it. Every other wallet reverts, on-chain, provably, not just hidden behind a UI.
- An AI vision check reads the photo first and judges whether it plausibly matches the claimed step, advisory only, it explains its reasoning but never blocks anything, the wallet check above is what actually gates the release.
- A custody map shows the whole journey, pool by pool, so anyone can see exactly how far a donation has traveled.
- A custodial email/password donor flow is also live, the backend signs and submits the donation itself, no wallet needed at all, just an email and a password.
- Once every checkpoint on a campaign is confirmed and every donation released, the campaign earns a Completion NFT, a permanent on-chain record that the whole journey was checked.
- For judges: there's a "Trigger live checkpoint" button on any real campaign card that logs a genuine, server-signed on-chain transaction on demand, no wallet needed, so this can be proven live during review, not just watched in a recording.
👥 Who this is for
- Malaysian donors who want to give to flood-relief and disaster campaigns without wondering whether the money actually reached anyone, no crypto experience or wallet required to donate.
- NGOs and campaign organizers who want a public, verifiable record of fund disbursement they can point to, instead of just their own word.
🛠️ How we built it
The core contract is Solidity 0.8.24, deployed to the Polygon Amoy testnet, using OpenZeppelin's ERC-721 for the Completion NFT. All the trust-critical logic, who can log a checkpoint, who can confirm it, who can mint the completion badge, is enforced with require() checks on msg.sender inside the contract, not hidden in frontend code.
Checkpoint evidence photos are pinned to IPFS through Pinata, and the resulting hash is what's actually stored on-chain, not the image itself. The AI evidence check runs through a three-provider fallback chain (Groq's vision model first, then Gemini, then GitHub Models) so a single provider's free-tier limit doesn't take the feature down.
The frontend is plain HTML, CSS, and JavaScript, no framework, talking to the contract through ethers.js v6. A small set of Vercel serverless functions handle the AI evidence check, an AI-generated campaign summary, a gas-relay drip for first-time donor wallets, and a judge-facing endpoint that logs a real on-chain checkpoint on demand so the product can be proven live, not just watched.
The design system was a deliberate choice too: a plain light UI, Inter typeface, no gradients or neon, nothing that reads as "crypto app." Someone who's never touched a wallet shouldn't feel like they've wandered into a trading terminal.
Challenges we ran into
Making the AI evidence check reliable enough to trust. I run it primarily on Groq's free tier (a vision-capable qwen model, 14,400 requests a day, generous enough to rely on day to day). But a demo can't assume any single provider stays up, so I built a three-provider fallback chain, Groq first, then Gemini, then GitHub Models, so one provider's outage or rate limit doesn't take the feature down. I also made sure the UI treats a failed check as informational only, never blocking, since the actual trust boundary was always meant to be the wallet permission check, not the AI.
Translating blockchain concepts for someone who's never used a wallet. "Checkpoint," "confirmer," and "on-chain" all needed a plain explanation the first time they appeared, so I built the fish ladder framing directly into the UI language and onboarding flow instead of leaving it as marketing copy.
Deciding what not to build. A few contract-level ideas came up along the way, letting anyone reclaim a stuck donation, requiring multiple confirmers instead of one, that would have meaningfully strengthened trust but touch core escrow logic. Building them under a hackathon deadline risked a bug I wouldn't catch before demo day, so I scoped them out deliberately and kept them as named future work instead of rushing them in.
🏆 Accomplishments that we're proud of
The wallet permission system isn't just described, it's demonstrable live: connect the wrong wallet and the confirm transaction actually reverts on a public testnet, in front of whoever's watching. The full lifecycle, donate, log, verify, confirm, release, mint, works end to end on real infrastructure, not a mockup.
Making that verifiable trust boundary approachable to someone who's never touched crypto, no jargon left unexplained, no wallet required just to browse, is the part I'm most proud of. It would have been easier to build this for people who already understand blockchain. Building it for people who don't was the actual goal.
🧠 What we learned
How to design Solidity access control around require(msg.sender == ...) so the permission logic can't be bypassed from the frontend. How to build an API integration that degrades gracefully instead of failing outright when a free-tier limit gets hit. And how much explaining a blockchain product needs before "trustless" and "on-chain" mean anything to someone outside the space.
🚀 What's next for CradleChain
Time-boxed refunds. If a checkpoint sits unconfirmed past a set number of days, the donor could reclaim that specific unreleased donation instead of it being stuck with no recourse. This is the single biggest trust win still on the table, deliberately scoped out for now since it touches core escrow logic and needs its own edge-case handling, partial refunds, a checkpoint confirming right as the window closes.
Multi-confirmer quorum. Move from one confirmer per campaign to a 2-of-3 quorum, so a single slow or colluding confirmer can't stall or fake a release alone.
Confirmer track record on the donate card. The confirm-rate data already exists on-chain (getConfirmerScore()) and already feeds the AI summary, next step is surfacing it as a plain stat right next to "Confirmer: X" so a donor sees the track record before giving, not just in Analytics afterward.
POL-to-RM display. RM to POL conversion works correctly today (rm_to_wei()), tested end to end. Showing campaign totals back in RM isn't built yet: the two hardcoded rates need to become one shared source first, then the display can be updated in one consistent pass.
Further out: deploying past testnet to a production chain, onboarding real NGOs beyond the seeded demo campaigns, and a mobile-friendly donor flow.
📂 Source code
Repo: https://github.com/zenyaa07/cradlechain
Live app: https://cradle-chain.vercel.app/frontend/index.html
Want to dig into the code itself? The README has a "Try it in thirty seconds" section right near the top with the fastest path to seeing it work, plus a full Setup section if you want to run it locally.
Built With
- alchemyapi
- css3
- d3.js
- django
- ethers.js
- github-models
- google-gemini
- groq
- hardhat
- html5
- ipfs
- javascript
- leaflet.js
- metamask
- node.js
- openzeppelin
- pinata
- polygon-amoy
- python
- render
- solidity
- sqlite
- vercel
- web3

Log in or sign up for Devpost to join the conversation.