Family tops up a student's allowance from anywhere, in about a second, for cents.
It only buys what a counter signed, at the signed price, inside the family's rules.
The student's AI agent can order, but a Monad contract (not the model) makes it take the cheapest match.
▶ Live app · Android APK v2.0.0 · Demo video (4:48) · Counter QR · Contract · Verify it
Monad Metropolis · Consumer Products & Payments
| Video | Length | What it shows |
|---|---|---|
| Demo | 4:48 | The story, real evidence for the problem, every feature on testnet, how it works, sponsor proof |
| Agora: cross-border payments | 1:59 | Passkey onboarding on two phones, an AUSD balance, Mom sends $20 from Pune and Riya's balance goes from $40 to $60 in one block, with the explorer and decoded Funded event as proof |
| Envio: end to end | 1:48 | The RPC's 100-block limit, then a real payment → Monad → HyperSync → HyperIndex handler → live GraphQL (same tx hash) → Receipts in the app |
| Mera: UX | 1:25 | Face ID sign-up, PRF → three keys (owner, agent, café), a fresh Face ID for Second Look, the passkey signing a café menu |
| Ad | 0:30 | The 30-second product spot |
Videos · The problem · What Tally does · See it · How it works · Proof: sponsor integrations · Why Monad · Try it · Repo · Honest status
Meet Riya. She studies in Toronto. Her parents in Pune send her lunch money, and her AI assistant now orders for her.
It isn't a feeling. These are the sources the video shows:
| Source | What it says | |
|---|---|---|
![]() |
World Bank, Remittance Prices Worldwide | Sending money costs 6.36% of the amount on average |
![]() |
ICEF Monitor / India MEA | More than 1.2 million Indian students in higher education abroad (about 1.88M in total, per MEA) |
![]() |
AgentLAB, arXiv 2602.16901 | LLM agents are highly susceptible to injected instructions in long-horizon tasks, including shopping |
![]() |
Greenlight help | Teen cards block a store or merchant category, not an item |
![]() |
Stripe shared payment tokens | Agent tokens cap a max_amount and a seller, not the item |
Greenlight controls the store. AP2 controls the cart. Tally controls the item, and the agent's choice of it. Full competitor table and gap analysis: docs/MARKET-GAP.md
| Agent wallets today | Caps & allowlists | Tally |
|---|---|---|
| How much? | ✅ | ✅ |
| To whom? | ✅ | ✅ |
| Which item? | — | ✅ |
| At the signed price? | — | ✅ |
| Cheapest match, or ask you? | — | ✅ |
| Family can fund it from abroad? | — | ✅ |
- One passkey, no seed phrase. Riya signs up with Face ID. Mera turns one passkey into three keys: her account, her agent's capped key, and a menu key if she runs a café.
- Rules on Monad. Coffee and food, $10 a purchase, $25 a day. They live in the Tally contract, and only her key can change them.
- Scan. Ask. Pay. The café's QR is a menu it signed. She says "oat latte". Her agent, behind a context firewall, sees only items and prices and picks the cheapest match. The contract checks everything, then pays the counter's registered account. Settled in under a second.
- Second look. If the agent is talked into the large one, it's $1.40 more than the cheapest coffee, so the contract won't let it pay alone. It waits for her Face ID.
- Family, from anywhere. Mom opens a link in Pune, uses her fingerprint and sends $20 in AUSD with a note, "For lunches this week". It arrives in about a second with a fee of $0.00, and can only be spent the way the rules allow.
- Attack your own agent. A fake counter, a phantom fee, a doubled price and an upsell all reach the contract, and all four are refused.
- On the record. Receipts, family top-ups, blocked attempts and Tally Points (real purchases only), indexed by Envio.
- Any café can join. The same passkey opens a counter. Its Face ID signs the menu, and a bond anyone can slash keeps it honest.
Real recordings of the live app on Monad Testnet (passkey, payments and top-ups are all on-chain):
Three proofs, one transaction. payOffer in contracts/src/Tally.sol:
| # | Check | Stops |
|---|---|---|
| 1 | Caller is the allowance's agent or owner; not paused or expired; counter active | Stolen keys, kill switch |
| 2–3 | ECDSA.recover(EIP-712 menu digest) == counter's registered key (domain binds chain + contract) |
Fake counters, forged menus |
| 4 | issuedAt ≤ now ≤ expiresAt; ≤ 60 s autonomous, ≤ 1 h only with the owner |
Replayed old QRs |
| 5 | Payout from the registry, never from the QR | Payee swap |
| 6 | The contract hashes the leaf itself and checks the Merkle proof | Price inflation, phantom add-ons |
| 7 | Category bitmap, per-purchase cap, rolling 24 h cap, balance | Out-of-policy spend |
| 8 | Cheapest match: price > floor (or under-bonded counter, or printed menu) needs the owner's EIP-712 Cosign |
Whisper upsells |
| 9 | Receipt nullifier keccak(capsule, counter, nonce, item) |
Double charges |
| 10 | transfer, then exact balance deltas on both sides |
Hidden drains |
Plus trustless slashing: anyone can slash 20% of a counter's bond when its own signed menu lists an item cheaper than the floor it certified. This works even after a key rotation, because every key the counter ever registered counts.
Everything below is real output from this repo and the live deployment. Raw text is in video/public/proof/; regenerate the images with python3 video/scripts/proof_png.py.
| Sourcify exact match | 32 contract tests | Receipt of a cosigned payment |
|---|---|---|
![]() |
![]() |
![]() |
Lesson we built for: Monad's reserve balance (10 MON, 3-block window) reverted our sponsor's second transfer. Drip.welcome now sends every welcome as one transaction:
Monad's public RPC caps eth_getLogs at about 100 blocks, so history can't come from RPC. The indexer handles Paid, Funded, CapsuleCreated, MerchantRegistered and Slashed (handlers) and is hosted on Envio Cloud. The app queries it in web/lib/activity.ts.
envio-cloud indexer get |
Live GraphQL query |
|---|---|
![]() |
![]() |
Endpoint: https://indexer.dev.hyperindex.xyz/a8a78bb/v1/graphql
web/lib/mera.ts: createPasskeyWithPrfOutput / getPasskeyPrfOutput → PRF → BIP-32 paths for the owner, a capped agent and a café menu signer → createSecp256k1SigningSession → toViemAccount. There's no seed phrase, extension or custody backend.
fund(id, amount, note) emits Funded(..., note), so the money arrives with its purpose. The testnet uses a 6-decimal AUSD stand-in, because AUSD is deployed only on mainnet (0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a). The token is a constructor argument.
/api/agent sends Kimi only item ids, categories, prices, floors and dictionary words. The server re-filters the payload, and the reply must be one item id in an allowed category. A deterministic rule is the fallback.
| Model list | Agent route |
|---|---|
![]() |
![]() |
A Trusted Web Activity over the live app (so Mera passkeys run in Chrome), verified with Digital Asset Links. See android/ and the release.
| APK + release | Digital Asset Links |
|---|---|
![]() |
![]() |
- Sub-second settlement makes tap-and-go honest: about 0.6 s from tap to a settled payment on testnet.
- Fees small enough for a $2 chai, and sponsored, so users never see gas.
- Built for Monad's rules: reserve-balance-safe one-transaction welcomes, gas from
estimateGas × 1.2(Monad bills the limit), and Envio for history.
Use a phone: install the Android app, or open tally-monad.vercel.app on any phone with synced passkeys (Google Password Manager, iCloud Keychain or 1Password).
- On a laptop, open tally-monad.vercel.app/counter, the demo café's live, re-signing QR.
- Phone: Get started → name → Create with Face ID. A $50 test credit arrives.
- Set up my allowance → Create allowance.
- Scan the laptop QR (or Use the demo café) → "oat latte" → Find the cheapest match → Pay.
- Try "large oat latte" → Second look → Approve with Face ID.
- Ask family on Home shares a top-up link; open it on another device and send $20.
- Red team tab → Run 4 attacks.
No test credentials are needed: the passkey is the account.
| Contract | Address |
|---|---|
| Tally | 0xe7d22F222a799218908997f837CF3e0Fd1c1e40a (Sourcify verified) |
| AUSD (test) | 0xad4B854A7eF968FDC53D4C8CC6590Dc24Cb536F1 |
| Drip | 0xafA3C3598E2051BDd615Fd7b7df8018fE22B625E |
| Path | What |
|---|---|
contracts |
Foundry: Tally.sol, MenuMerkle.sol, Drip.sol, MockAUSD.sol; 32 tests including the attack suite |
web |
Next.js app in the original Tally design; 65 vitest tests + 1 live testnet run |
indexer |
Envio HyperIndex, hosted on Envio Cloud |
android |
Android app (Trusted Web Activity + Digital Asset Links) |
video |
Remotion demo video: storyboard, recorder, evidence capture, voice, music |
docs |
Market gap, Monad notes, verify guide, submission kit, plan |
cd contracts && forge test # 32 tests
cd web && pnpm install && pnpm test # 65 tests
cd indexer && pnpm install && pnpm test # handler tests
E2E=1 pnpm -C web vitest run test/e2e.live.test.ts # live Monad Testnet run
cd video && npx remotion render Tally out/tally-monad-demo.mp4 # the demo video- Testnet only. AUSD is a stand-in token on testnet; the mainnet address is documented.
- Not audited. Welcome credits and fees come from a sponsor wallet; on mainnet that becomes a paymaster or a fee in AUSD.
- Bonds and the compensation pool can't be withdrawn yet.
- Ported from Tally on Solana. Research figures are other teams' published results, not our measurements.
Apache-2.0





























