Here's a rewritten version, expanded and in a more natural, conversational voice, with the dash-heavy structure replaced by full sentences.
What it does
Circle lets you pay someone the way you'd naturally describe a payment to a friend: you just say the amount and the person's name out loud. Behind the scenes, Circle figures out exactly who you mean, works out which payment rail that person actually accepts, and settles the transaction while keeping every sensitive detail off any public ledger.
When you speak a payment, Circle listens for the amount and the recipient, shows you a confirmation screen so you can double check before anything moves, and then settles it. The identity piece is where things get interesting. Names aren't unique. There might be three "Ethan" in your contacts, or someone you've never paid before who shares a name with someone you have. So instead of trusting a spoken name at face value, Circle resolves it down to one specific identity made up of a handle, a jurisdiction, and an opaque account id that never gets exposed. If more than one person matches, you're asked to pick the exact one before anything proceeds. And if the person you're trying to pay isn't already a contact Circle recognizes, it runs an explicit verification step, checking a brand registry and KYC records, and it simply won't let you hit accept until that verification clears.
Circle is also built to work across currencies and rails without you having to think about it. Someone paying in rupees can send money to someone who only accepts dollars, and Circle handles the quoting, the routing, and the settlement itself. Every one of these cross asset payments is bound together by something we call a RouteProof, which locks in the sender, the receiver, the source and target assets, the quote, the route, an expiry time, and your acceptance, all in one cryptographic commitment.
That binding is what makes tamper rejection possible. If a malicious router tried to quietly swap your intended USD payment into BTC after you'd already authorized it, the route commitment simply wouldn't match anymore, and the settlement would be refused. Not because of a rule someone wrote, but because the math no longer checks out.
We also built in recipient approved refunds, so a refund only actually happens once the person receiving it agrees to it, rather than balances silently reversing on their own. And because we take honesty seriously, we included a "Real vs demo" view that spells out plainly what parts of the app are genuine zero knowledge proofs running against Midnight and what parts are still sandboxed for demo purposes. There's also a one tap "Voice / test" mode that drops you straight into the payment screen with a pre provisioned test wallet, so you can try the whole flow immediately without any setup.
How we built it
The frontend is React, TypeScript, and Vite, wrapped in a phone framed interface. Voice capture includes confidence and shape gating so we're not just guessing at what was said, and the confirm sheet layers identity verification, disambiguation, and brand checks all into one place rather than spreading them across separate screens.
On the zero knowledge and settlement side, we wrote Compact circuits that run on Midnight. Along the gold path, we use real proof server SNARKs, and if the proof server isn't available for some reason, the system gracefully falls back to compact runtime instead of just breaking. The RouteProof mechanism binds the entire payment intent together, which is what lets us cryptographically detect if anyone tries to tamper with a route after it's been authorized.
The backend runs on Node and Express, handling quoting, route planning, a hard compliance gate that has to pass before any settlement can happen, recipient verification through our registry and a sandbox KYC provider, and rail adapters connecting to a Stripe test sandbox, a sandbox PSP, and FX services. All of the state, payments, quotes, routes, and ledgers, gets persisted in SQLite, so nothing gets wiped out if the app restarts.
For testing, we put together a Vitest suite with over 150 tests, a smoke and rehearsal script specifically for judges that walks through the full voice to proof to tamper rejection to settle to receipt path, and Playwright end to end tests covering our pilot flows.
Challenges we ran into
The biggest surprise was how much trouble a simple name could cause. Verifying someone by name alone turns out to be almost meaningless once you accept that names repeat all the time. We had to go back and rebuild verification around unique identities and add a proper ambiguity picker, all while trying not to turn what should feel like a quick confirm sheet into a clunky form full of fields.
Making tamper rejection actually provable, and not just something we could describe in words, took real work. Getting the RouteProof binding right, and then figuring out how to demonstrate the rejection clearly enough that someone without a technical background could watch it happen and understand what just occurred, took several rounds of iteration on both the cryptography and the UI.
We also learned the hard way how much complexity comes from making things durable. Moving payments, quotes, routes, and rail ledgers out of in memory maps and into a real persistent store, while still keeping our tests isolated from each other, surfaced a bunch of tricky bugs around hydration and how data gets bound together on restart.
Demo reliability was its own challenge entirely. Live voice input combined with live proof generation is about as risky as a demo can get, since either one can fail unpredictably in front of judges. So we built a one tap "Run judge demo" option along with a test wallet path, specifically so the happy path we show off never depends on a microphone behaving itself in a noisy room.
And maybe the hardest challenge wasn't technical at all. It was being honest about what "real" actually means in our submission. We made a deliberate choice to draw a clear, visible line between genuine ZK settlement and the sandboxed rails we used to make the demo possible, and to document that line rather than letting the presentation blur it.
Accomplishments that we're proud of
We're proud that we have an actual working zero knowledge path running on Midnight, with a live tamper rejection demo that really happens in front of you rather than something mocked up to look convincing.
We're proud of getting cross asset universal payments working, where someone can pay in INR and have it land as USD on the other end, complete with a quote, a route, a receipt, and a proof that binds the whole thing together.
We're proud that identity sits at the center of the payment flow instead of being an afterthought. Unique identity resolution, disambiguating between people who share a name, and a real KYC and verification gate are all built directly into the pay flow itself, not bolted on separately.
We're proud that our store and our compliance gate are durable enough to survive a restart, which is what actually moves this from feeling like a toy demo toward something that could function as a capped pilot.
And we're proud of the honesty in the experience itself. A lean, uncluttered menu, a fast "Voice / test" path for trying things immediately, and a "Real vs demo" explanation that tells people the truth about what they're looking at instead of letting them assume everything is production ready.
What we learned
We learned that privacy and provability aren't actually in tension with each other the way they might seem at first. Zero knowledge proofs let you hide the sensitive data completely while still proving, with certainty, that all the rules were followed correctly.
We learned that identity is genuinely the hardest part of building a payment system, harder than the actual transfer of money. Voice makes that obvious almost immediately, because the moment you let someone just say a name out loud, you're forced to confront how ambiguous names really are. Getting disambiguation and verification right turned out to be where trust is actually won or lost, more than anywhere else in the product.
We learned that you need to bind the entire intent of a transaction, not just the transfer itself. Committing to the whole route upfront, before settlement, is exactly what makes silent tampering after the fact something you can actually detect rather than just hope doesn't happen.
And we learned that judges, like users, trust honesty. Clearly labeling what was real and what was sandboxed earned us more credibility than it would have cost us to just let everything look finished and production ready.
What's next for Circle
The next big milestone for us is a single real testnet settlement, start to finish. A stablecoin transfer with an actual on chain transaction hash bound directly into the receipt, which would close the last real gap between "this looks real" and "this is real."
Beyond that, we want to move past sandbox rails entirely and bring in licensed UPI, bank, and card partners, all operating behind the same universal adapter we've already built.
We're also interested in building out richer, more agentic payment flows. Scheduled payments, conditional payments, spending policies, and multi party approvals, all still proven privately through the same zero knowledge foundation.
On the compliance side, we want to move to production grade KYC and AML by wiring in a provider like DigiLocker or an Onfido class service in place of our current sandbox issuer, with selective disclosure built in from the start.
And finally, we want to harden this toward an actual pilot. That means real ops dashboards, automated refund and dispute handling, and getting to a capped pilot go live once our compliance and settlement gates are fully green across the board.
Built With
- better-sqlite3
- blockchain
- compact
- cryptography
- css
- docker
- express.js
- html
- javascript
- midnight
- node.js
- playwright
- pwa
- react
- rest-api
- sqlite
- stripe
- typescript
- vite
- vitest
- web-speech-api
- webauthn
- zero-knowledge-proofs
- zk-snarks
Log in or sign up for Devpost to join the conversation.