Curb
View live ↗What it is
Your bank card has a daily limit. Your wallet doesn't.
Curb is a vault contract that holds your funds and caps how much can leave per day. Sends under the cap confirm in a few hundred milliseconds and go to any normal address — no claim step, no integration. Sends over the cap are rejected by the contract itself, not by a dialog you can click through.
Lowering your limit is instant. Raising it is timelocked, so a stolen key can't widen the limit and drain you in the same breath — it has to ask, then wait, in the open, where you can cancel it.
Why it exists
I'm not a crypto person. I have about $20 in a wallet I opened once and never went back to, because sending felt like defusing something: a long string of gibberish, an amount, no ceiling and no undo. My bank card has never scared me like that, and the reason is boring — it has a daily limit. I never needed to understand banking to be protected.
Crypto wallets have no such thing. That's the problem: not that crypto is hard, but that it has no seatbelt for people who don't want to become experts first.
How it works
The load-bearing detail is custody. The contract holds the money, so your own key can't route around your own limit — funds leave through send() or they don't leave. Withdrawing to yourself is just a send, so there's no uncapped back door.
I tried to build this with EIP-7702 first, which looks like the obvious tool. It doesn't work: a delegated EOA keeps its key, delegated code only runs when the account is called (not when the key signs a plain transaction), and the delegation can be cleared at will. A 7702 "spending limit" is a switch the attacker flips off. You can't limit a key you still hold — you have to not hold the money.
Why Monad: a spending limit works on any chain. What a fast chain changes is whether you'd keep it switched on. Curb adds a contract hop to every send; on a 12-second chain that's a tax you feel every time, and a safety feature you feel is one you turn off. With eth_sendRawTransactionSync the write returns its receipt in the same request, so the protection stops being something you notice.
What it doesn't do
Published on the landing page, not buried in a README. A stolen key can still spend the cap every window, and take roughly twice the cap in one burst by straddling a window reset — fixed windows, pinned by a test rather than hidden. It can request a raise you have to notice and cancel. It can set the cap to zero and freeze the account for a full delay. Your own exit is capped too; that's the product, not a bug.
Curb bounds the damage. It doesn't make you un-hackable.
Current status
Live at curb-pink.vercel.app. Contract 0xAf1B0624C2086eFA818F84d00BCF6411380A6A16 on Monad Testnet, verified. 30 tests including a reentrancy attacker, a reverting recipient, and fuzz checks on cap-integrity and solvency. Independently security-reviewed — two findings fixed before shipping: lowerCap didn't clear a pending raise (so the obvious panic move left a thief's raise alive), and an unbounded raise delay could overflow and strand funds permanently.
The landing page proves the cap without a wallet: it simulates a real send via eth_call and decodes the contract's own CapExceeded error out of the revert. No hardcoded success path — if the contract stopped enforcing, the page would say so.
Built solo for the Monad Spark hackathon.
Credit
This isn't a new idea. Argent v1 shipped daily transfer limits years ago; Safe has allowance modules. The idea that your wallet should have a ceiling is old, correct, and somehow still not the default — which is the actual point.
Stack
Solidity (Foundry), Monad Testnet, Next.js, wagmi/viem, Tailwind, Vercel.