Inspiration Showing up is half the battle. Most goals die not because they were too hard, but because nobody was watching. We wanted something stricter than a habit tracker and warmer than a guilt trip: put real money on the line, but route it to charity if you flake so you can't even feel cheated. Don't go to the gym Tuesday? The Red Cross gets your $5. Turns out that's surprisingly motivating.
What it does You set a goal in the web app, pick a spot on the map, a time, and a stake in XRP. The stake gets locked in an escrow on the XRP Ledger testnet, and the payout destination is the charity you chose at creation, not us. When you show up, you take a selfie. The server pulls GPS and capture time straight from the photo's EXIF metadata and checks that you were inside the geofence at the right time. Goals can be a one-off ("gym at 7am tomorrow") or a multi-week recurring contract. You pick the days and times. Miss any session, and the whole pooled stake goes to charity. Gemini takes a second look at the image and says whether it plausibly matches the goal, but that runs as advisory and never overrides the on-chain verdict.
How we built it
Next.js (App Router) on Vercel with a Vite + React frontend. MongoDB Atlas for users, goals, and proofs. The on-chain layer uses the XRP Ledger's native escrow primitives (EscrowCreate, EscrowFinish, EscrowCancel) rather than a custom smart contract. The big architectural call here was the escrow's Destination field. We point it to the charity address itself, so failed stakes move from the user to the charity atomically. The app physically cannot intercept funds. Stakes also resolve without a server cron. The next time you open the app, any past-due goal is finalized on-chain in the same request. Cursor agents helped us scaffold API routes from our schema doc and run frontend and backend work in parallel.
Challenges we ran into The EXIF pipeline was a bloodbath. iPhones embed timezone info. Most Androids don't. Because of this, a 7am Kansas selfie was getting interpreted as 7am UTC and rejected for being hours outside the window. Then the GPS parser was returning unsigned magnitudes, which silently flipped Kansas (-95° longitude) onto +95° (somewhere in western China) and failed every geofence check. We added hemisphere-aware parsing and a client-side ISO timestamp fallback. Wiring three independent systems (Mongo writes, XRPL transactions, and the React UI) into a single coherent state machine produced its own ladder of fun race conditions.
Accomplishments that we're proud of
Every bet card on your dashboard has a live link to the XRPL testnet explorer for both the EscrowCreate and the eventual payout, meaning you can prove the money actually moved. We finished the core loop early enough to add real polish. We shipped a dedicated docs page explaining the system inside the app itself, a dev mode for force-resolving bets during demos, lazy on-chain expiry so the system needs no background workers, and multi-week recurring contracts on the final day. The whole thing runs on free tiers.
What we learned
A lot about how XRPL escrows actually work under the hood. FinishAfter and CancelAfter are the difference between a refundable bet and a stuck one, and getting them wrong means the user's stake is unrecoverable until a date you set hours ago. Also: never trust EXIF metadata to be consistent across phone manufacturers, and never trust a verification stack you haven't tested on a friend's Android.
What's next for BetOnMe Friend-vs-friend wagers with shared escrows, leaderboards for streaks and follow-through rates and partial-credit recurring contracts (hit 3 of 5 days and get most of your stake back). Finding a way to allow more flexibility is also important, but it can't be implemented in a way to diminish the core concept.




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