Spend Leash is a small contract that holds your agent's budget. The agent can pay for APIs, data and other agents. The contract refuses anything over your caps, to anyone you did not approve, or for a request already paid.
Status: design preview and interactive browser prototype. Not audited, not deployed. The planned pilot will run on a testnet. Source code is published with the pilot.
From the factory, in the token your agent pays with. maxFeeBps is the highest fee you accept; the pilot fee is 0.
// one vault per agent (or per budget)
vault = factory.createVault(token, /* maxFeeBps */ 0)
token.approve(vault, amount); vault.deposit(amount)
Every vault starts paused, with the payee allowlist on.
vault.setAgent(agentAddress) // the only key your agent holds vault.setPolicy(perPayment, per24h, true) // caps in token units · allowlist on vault.setPayee(weatherApi, true) // approve who may be paid vault.setPaused(false)
When an API answers 402 Payment Required, check the leash for free, pay once, then retry with the receipt.
// agent side (proposed pseudocode; adapter not published yet) res = fetch(url) if (res.status == 402) { req = res.json().accepts[0] // payTo, asset, amount, nonce ref = keccak256(chain, asset, payTo, resource, amount, nonce) [ok, reason] = vault.canPay(req.payTo, req.amount, ref) if (!ok) return stop(reason) // e.g. DailyLimitExceeded tx = vault.pay(req.payTo, req.amount, ref) res = fetch(url, { headers: { 'X-PAYMENT': base64({ nonce, tx }) } }) }
Before serving data, the proposed seller adapter must check the transaction:
Paid event comes from a vault the factory created (factory.isVault), in the right token.ref match the request; the nonce is used once.| Function | Who | What it does |
|---|---|---|
pay(payee, amount, ref) | agent or keeper | Pays once per ref, inside every rule, and emits Paid(n, payee, ref, amount, fee). |
canPay(payee, amount, ref) | anyone | Free dry run of the rules. Returns (ok, reason). It does not check who is calling, so pay can still revert with Unauthorized if the caller is not the agent or keeper. |
spentLast24h() · remainingToday() | anyone | The rolling window: the current hour plus the 24 before it. No midnight reset. |
setPolicy · setPayee · setAgent | keeper | The leash itself. |
setPaused · setHerdSwitch | keeper | Stop new payments now, or opt in to a shared crash brake (fails closed if broken). |
deposit · withdraw | anyone · keeper | Withdraw always works, also while paused. |
| Revert reason | Meaning |
|---|---|
PerPaymentLimitExceeded | Payment plus fee is over the per-payment cap. |
DailyLimitExceeded | Would pass the 24-hour budget. |
PayeeNotAllowed | Allowlist is on and this payee is not on it. |
RefAlreadyPaid | This request was already paid. |
TradingPaused · HerdHalted | The keeper paused, or the Herd Switch is on. |
InsufficientBalance | Not enough funds in the vault. |
| Role | Can | Cannot |
|---|---|---|
| Keeper (you) | Set every rule, pause, withdraw | Pay past the caps either |
| Agent | Pay inside the rules | Withdraw, change rules, or pay unapproved payees (while the allowlist is on) |
| Factory owner (BRAWL) | Set the fee for new vaults, max 1% | Touch existing vaults, their fee, or their funds |
| Herd Switch guardians (opt-in) | Pause new payments, max 24 hours per halt (you can opt out) | Move funds |
Applications currently go by DM. This form prepares text for you to copy and send; it does not submit your details.
We read every application ourselves and use your answers only to run the pilot. Ask us any time to delete them. We never ask for keys or seed phrases.