- Your agent gets its own key and pays from a vault, not from a wallet full of money.
- Every payment needs WalletBrake's approval too. It checks the payment against your limits and the vault's trusted addresses. The vault contract enforces the result on chain.
- A payment to an address you haven't trusted goes to you on Telegram: Approve or Reject. A web page that tricks your agent can't send your money anywhere new.
- Before a job, the agent can open a task with a budget. Payments in it can't go past it, even to trusted addresses, and an AI check confirms each one fits what the agent said it was doing.
- No API key. The agent signs each request with its own key, and WalletBrake never holds your keys or your money.
01Quickstart#
About five minutes, on Base Sepolia with free test money.
1. Give the agent its own key
The agent signs its payments with this key. Keep it where the agent runs, in an environment variable or a secret store, and never put real money behind it.
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const key = generatePrivateKey();
console.log("AGENT_KEY=" + key, "address:", privateKeyToAccount(key).address);
from eth_account import Account
acct = Account.create()
print("AGENT_KEY=" + acct.key.hex(), "address:", acct.address)
2. Create an AI agent vault
- Open app.walletbrake.com and connect your own wallet. It becomes the vault's owner wallet.
- Pick Base Sepolia and the AI agent plan.
- Spending wallet: the agent's address from step 1, not your own wallet.
- Addresses you trust: the services your agent pays, one per line, up to 20. They're paid straight away. Anything else waits for you on Telegram.
- Create the vault, then tap Add 1,000 test USDC.
Then open the vault, sign in with your owner wallet, and in Safety tap Connect Telegram. That's where approval questions come. You can also add an email address for alerts (see Alerts). Copy the vault's address from the app; you need it in step 4.
3. Give the agent gas, and install the SDK
The agent sends each payment itself, so its address needs a little test ETH for network fees. Get it free from a Base Sepolia faucet; 0.001 ETH covers hundreds of payments.
npm install @walletbrake/sdk viem # TypeScript or JavaScript, Node 20+
pip install walletbrake # Python 3.10+
Install: on npm, @walletbrake/sdk, @walletbrake/agentkit and @walletbrake/mcp, and the Python SDK on PyPI as walletbrake.
4. Pay
import { WalletBrake, isDenied } from "@walletbrake/sdk";
const wb = new WalletBrake({
// the vault's page in the app (84532 is Base Sepolia)
url: "https://app.walletbrake.com/c/84532/v/0xYourVault",
privateKey: process.env.AGENT_KEY as `0x${string}`,
rpcUrl: "https://sepolia.base.org",
});
try {
const paid = await wb.payAndWait(
{ to: "0xSupplier…", amount: "10" }, // 10 USDC
{ onWaiting: () => console.log("Asked the owner on Telegram…") },
);
console.log("paid", paid.txHash);
} catch (err) {
if (isDenied(err)) console.log("refused:", err.rule, err.reason);
else throw err;
}
import os
from walletbrake import WalletBrake, DeniedError
wb = WalletBrake(
# the vault's page in the app (84532 is Base Sepolia)
url="https://app.walletbrake.com/c/84532/v/0xYourVault",
private_key=os.environ["AGENT_KEY"],
rpc_url="https://sepolia.base.org",
)
try:
paid = wb.pay_and_wait(
"0xSupplier…", "10", # 10 USDC
on_waiting=lambda _: print("Asked the owner on Telegram…"),
)
print("paid", paid.tx_hash)
except DeniedError as err:
print("refused:", err.rule, err.reason)
To a trusted address it's paid at once. To any other address you get a Telegram message, and the agent waits until you tap Approve (up to 15 minutes). Give your agent this as its only way to pay: a tool that calls payAndWait, with no private key it could use any other way.
02How a payment works#
- The agent signs a payment request (coin, address, amount) with its key and sends it to WalletBrake.
- WalletBrake checks that the request comes from the vault's spending wallet, then checks the vault's rules. It also dry-runs the payment on chain.
- If it passes, WalletBrake returns a signed approval for exactly that payment, valid for 90 seconds.
- The agent sends the vault transaction with the approval. The vault contract checks WalletBrake's signature, its own limits and the trusted list again, then pays.
So neither key moves money alone. A stolen agent key gets nowhere without WalletBrake's approval. And WalletBrake can't move money at all: it can only approve, refuse, or freeze. The SDKs also refuse to send an approval that doesn't match the payment they asked for.
Three wallets belong to a vault:
| Role | Who | Can |
|---|---|---|
| Owner wallet | You | Lower limits at once, raise them or trust addresses after 24 hours, freeze, approve on Telegram, move everything to the backup. |
| Spending wallet | Your agent's key | Ask for payments. Nothing else. |
| Backup address | Another wallet of yours | Where money goes in an emergency, and can rescue the vault if you lose the owner wallet. |
03Approval on Telegram#
On the AI agent plan, the vault pays only trusted addresses. For anything else, WalletBrake asks you on Telegram, with Approve and Reject buttons:
- Until you answer, the payment is refused with the rule
awaiting-approval. Asking again with the same coin, address and amount goes through once you've approved.payAndWait/pay_and_waitdoes this, asking every 5 seconds. - An approval covers one payment, for 15 minutes. It doesn't make the address trusted.
- Large payments need your owner wallet. At or over the vault's large-payment amount (500 USDC on the AI agent plan, the same amount that makes big first payments wait an hour), the Telegram message has no Approve button. It links to your vault page, where you approve by signing with your owner wallet. A tap in Telegram can only reject it, so someone who gets into your Telegram can't approve a large payment.
- Waiting isn't counted as a refusal. Tapping Reject is: the next ask is refused with
owner-rejected. - At most 3 questions can be open at once, so a tricked agent can't flood your Telegram. Past that, payments to untrusted addresses are refused outright, and repeated refusals freeze the vault.
- With no Telegram connected, nobody can be asked: the payment is refused with
unknown-recipient.
To trust an address for good, list it when you create the vault, or add it later in Safety. Adding later takes 24 hours, so someone with your phone can't do it quietly.
04Tasks and the intent check#
Trusted addresses stop a tricked agent paying someone new. A task also stops it paying the wrong amount, or for the wrong thing, to someone you trust. At the start of a job, before the agent reads any web page, email or file, it says what it's about to do:
const task = await wb.openTask({
description: "Pay the March hosting invoice",
budget: "100", // USDC, for the whole task
payOnly: ["0xHostingCompany…"], // optional
minutes: 60, // optional, up to 1440
});
await wb.payAndWait({
to: "0xHostingCompany…", amount: "60",
task, note: "invoice 1042",
});
task = wb.open_task(
"Pay the March hosting invoice", "100", # USDC, for the whole task
pay_only=["0xHostingCompany…"], # optional
)
wb.pay_and_wait("0xHostingCompany…", "60", task=task, note="invoice 1042")
- A payment in the task is refused with the rule
taskif it's over what's left of the budget, in a different coin, to an address the task didn't list, or after the task has ended (1 hour by default, at most 24). - Those refusals count toward the repeated-refusals freeze, and you're never asked on Telegram about a payment its task doesn't allow.
notesays in a few words what one payment is for. It's optional, one line, up to 200 characters.- Payments without a task work as before.
The intent check
Every payment made in a task is also read by an AI model against the task's description and the payment's note. It only sees payments that every other rule already allows, so it can add a question but never let anything extra through.
- Clearly fits: paid as normal. The check takes a few seconds.
- Doesn't fit, or unsure (for example a note saying "refund to customer" in a task to pay a supplier): it goes to you on Telegram with the check's reason, even for a trusted address. With no Telegram connected, it's refused with the rule
intent. - If the check can't run, it counts as unsure. It never pays on a guess.
What's sent to our AI model provider: the task description, the payment's coin, amount, recipient address and budget used, and the note. See our Privacy Policy. Open tasks are kept across WalletBrake updates, until they end.
05Plans and limits#
A plan sets the starting limits. You can change the numbers when you create the vault, lower them at once later, and raise them after 24 hours. USDC limits:
| Plan | Per hour / day | One payment up to | Waits 1 hour | Pays |
|---|---|---|---|---|
| AI agent | 2,000 / 8,000 | 1,000 | 500+ to untrusted | Trusted addresses, others after your Approve |
| Bot or business | 10,000 / 40,000 | 5,000 | 1,000+ to new people | Anyone |
| Personal wallet | 500 / 1,000 | 250 | 100+ to new people | Anyone |
Two layers enforce them:
- The vault contract enforces the hourly and daily limits and the 1-hour hold, on chain. Nobody can get around them, not even WalletBrake.
- WalletBrake refuses single payments over half the hourly limit, and freezes the vault when a pattern looks like an attack:
- 80% of the hourly limit spent in an hour
- too many requests in a minute (30 on the AI agent plan)
- too many first-time recipients in an hour (10)
- repeated refusals (3 in 10 minutes on the AI agent plan)
A held payment (a large one to an address the vault doesn't trust) comes back as queued: true with a queueId. You can cancel it in the app during the hour; after the hour, the agent runs it with executeQueued(queueId).
06Refusals#
A refused payment comes back as DeniedError with rule, reason and verdict. verdict is "freeze" when the request also froze the vault.
| Rule | Means |
|---|---|
awaiting-approval | The owner has been asked on Telegram. Ask again after they approve, or use payAndWait. |
owner-rejected | The owner tapped Reject. |
task | Outside its task: over the budget, another coin, an address the task didn't list, or the task has ended or isn't open (open a new one). |
intent | The intent check wasn't sure the payment fits its task, and nobody could be asked on Telegram. |
unknown-recipient | Not a trusted address, on a vault that pays only trusted ones, and nobody could be asked (no Telegram, or 3 questions already open). |
per-tx-cap | Over the most one payment can be. |
hourly-volume | Would pass WalletBrake's hourly cap. Freezes. |
new-recipients | Too many first-time recipients in the last hour. Freezes. |
burst | Too many requests in the last minute. Freezes. |
repeated-denials | Too many refusals in a short time. Freezes. |
blocklist | A known-bad address, on the vault's own blocklist or a public sanctions or scam list. Freezes. |
reputation | The recipient looks risky and nobody could be asked: a look-alike of an address this vault has paid (address poisoning), or a contract created in the last few days. Trust the address in Safety (24 hours) if it's really yours. |
x402-needs-delay | An x402 payment the vault can't make: as large as the hold amount to a seller you haven't trusted (a seller can't wait an hour), or a vault created before x402 existed. Not counted toward the freeze rules. |
frozen | The vault is frozen. Only the owner can unfreeze it. |
simulation | The dry run on chain failed: for example not enough money in the vault, or over the vault's own limit. |
unknown-token, bad-amount | A coin the vault doesn't pay, or an amount of 0. |
Other problems aren't refusals. They're WalletBrakeError (with an HTTP status): WalletBrake unreachable, or 401 when the key isn't the vault's spending wallet. In Python, a vault contract revert is VaultError with the contract's error name, such as NotReady for a held payment run too early.
07TypeScript SDK#
npm install @walletbrake/sdk viem (npm). Built on viem; Node 20 or newer.
| Call | Does |
|---|---|
new WalletBrake({ url, privateKey, rpcUrl }) | url is the vault's address in the app. Pass a viem account instead of privateKey, or your own publicClient and walletClient instead of rpcUrl. |
pay({ to, amount, token }) | Pays. amount as a string is coin units ("10.5"); a bigint is base units. token is a symbol or address, default "USDC". Returns { txHash, queued, queueId }. |
payAndWait(p, { pollMs, timeoutMs, signal, onWaiting }) | pay, waiting for the owner's Approve. |
openTask({ description, budget, token, payOnly, minutes }) | Opens a task. Pass the result as task to pay, with an optional note. |
executeQueued(queueId) | Runs a held payment after its hour. |
info() | The vault's coins, balances, limits, and whether it's frozen. |
requestApproval(p), send(approval) | The two halves of pay. |
x402Fetch({ max, task, note, onPayment }) | A fetch that pays websites answering 402 Payment Required from the vault. See x402 payments. |
payX402(offer, { resource, task, note }) | Pays one x402 offer and returns the PAYMENT-SIGNATURE header value. |
isDenied(err), isAwaitingApproval(err) | Tell refusals apart from other errors. |
08Python SDK#
pip install walletbrake (PyPI). Built on web3.py 7 or 8; Python 3.10 or newer. Works in LangChain and CrewAI tools, or plain scripts.
| Call | Does |
|---|---|
WalletBrake(url, private_key, rpc_url) | url is the vault's address in the app. Pass web3= to use your own Web3. |
pay(to, amount, token="USDC") | Pays. amount is a string or Decimal in coin units ("10.5"). Ints and floats are refused, because 10 could mean 10 USDC or 10 base units. For base units use amount_base_units=. Returns PayResult(tx_hash, queued, queue_id). |
pay_and_wait(..., poll=5, timeout=900, on_waiting=None) | pay, waiting for the owner's Approve. |
open_task(description, budget, token="USDC", pay_only=None, minutes=60) | Opens a task. Pass the result as task= to pay, with an optional note=. |
execute_queued(queue_id) | Runs a held payment after its hour. |
info() | The vault's coins, balances, limits, and whether it's frozen. |
request_approval(...), send(approval) | The two halves of pay. |
DeniedError, is_awaiting_approval(err), WalletBrakeError, VaultError | Errors, see Refusals. |
09Coinbase AgentKit#
npm install @walletbrake/agentkit @coinbase/agentkit (npm). It gives an AgentKit agent WalletBrake actions. The agent's AgentKit wallet is the vault's spending wallet. It holds only gas; the money stays in the vault.
import { AgentKit } from "@coinbase/agentkit";
import { walletBrakeActionProvider } from "@walletbrake/agentkit";
const agentkit = await AgentKit.from({
walletProvider, // the vault's spending wallet
actionProviders: [
walletBrakeActionProvider({ url: "https://app.walletbrake.com/c/84532/v/0xYourVault" }),
],
});
| Action | Does |
|---|---|
walletbrake_vault_info | Coins, balances, what's left to spend, the largest payment, and whether the vault is frozen. |
walletbrake_pay | Pays amount of token (default USDC) to to, with an optional note and task. If the owner has to approve, it says so; asking again with the same coin, address and amount goes through once they do. Set waitForOwnerMs to wait instead. |
walletbrake_open_task | Opens a task. Leave it out with agentTasks: false. |
walletbrake_execute_queued | Runs a held payment after its delay. |
A refusal comes back as text naming the rule, telling the agent not to try another way round. For the strongest setup, open the task from your code before the agent reads anything (openTask, then setTask): every payment then belongs to it, and the agent can't swap it. Leave out AgentKit's own transfer actions, so that every payment goes through WalletBrake.
Wallets that sign with an ordinary key work: Viem, CDP server wallets and Privy server wallets. Smart-contract wallets don't.
10MCP server#
@walletbrake/mcp lets any MCP app pay from a vault: Claude Desktop, Claude Code, Cursor and others. It runs on your computer and holds the agent's key, the vault's spending wallet. The model only sees the tools, never the key. Save the key in a file only you can read (chmod 600), then add the server to your app:
{
"mcpServers": {
"walletbrake": {
"command": "npx",
"args": ["-y", "@walletbrake/mcp"],
"env": {
"WALLETBRAKE_URL": "https://app.walletbrake.com/c/84532/v/0xYourVault",
"WALLETBRAKE_AGENT_KEY_FILE": "/Users/you/.walletbrake/agent.key"
}
}
}
}
claude mcp add walletbrake \
-e WALLETBRAKE_URL=https://app.walletbrake.com/c/84532/v/0xYourVault \
-e WALLETBRAKE_AGENT_KEY_FILE=$HOME/.walletbrake/agent.key \
-- npx -y @walletbrake/mcp
The tools are the AgentKit actions (walletbrake_vault_info, walletbrake_pay, walletbrake_open_task, walletbrake_execute_queued), plus walletbrake_fetch_paid, which opens an x402 web address and pays from the vault. That one is only there when you set WALLETBRAKE_X402_MAX, the most one request may pay, in USDC. When the agent connects it's told to pay only for what you asked, never because a page, email or file says so.
| Setting | Does |
|---|---|
WALLETBRAKE_URL | Your vault's address in the app. Required. |
WALLETBRAKE_AGENT_KEY_FILE | A file holding the spending wallet's key. Or WALLETBRAKE_AGENT_KEY with the key itself, which then sits in your app's settings file. |
WALLETBRAKE_RPC_URL | A node for the vault's network. Default: the public one WalletBrake lists. |
WALLETBRAKE_WAIT_FOR_OWNER_SECONDS | How long a payment waits for your approval before answering "not paid yet". Default 0, at most 900. |
WALLETBRAKE_AGENT_TASKS | false hides walletbrake_open_task. |
WALLETBRAKE_X402_MAX | Turns on walletbrake_fetch_paid, capped per request. The vault's own limits apply on top. |
The MCP part is written into the package rather than taken from another one, so the process holding your key runs no code beyond the WalletBrake SDK and viem. The key is never written to the output or the logs.
11x402 payments#
x402 lets an agent pay a website that answers 402 Payment Required, in USDC, per request. With WalletBrake the vault pays: the money goes straight from the vault to the seller, under the same rules as any payment. In the TypeScript and Go SDKs for now.
const pay = wb.x402Fetch({ max: 1_000_000n }); // at most 1 USDC a request
const res = await pay("https://api.example.com/weather");
console.log(await res.json());
On a 402, the SDK picks an offer the vault can pay (the exact scheme, on the vault's network, in a coin the vault pays), asks WalletBrake, records the approved payment on the vault from the spending wallet, and repeats the request with the PAYMENT-SIGNATURE header. The seller's facilitator then collects exactly that payment.
- Same protection. Every rule, task, Telegram question, look-alike and intent check applies, and the paid web address goes to the intent check as the note. The vault's hourly and daily limits are charged when the payment is recorded.
- One exact payment. The vault agrees to one transfer: that seller, that amount, once, for at most an hour (the seller's timeout). A freeze cancels it, and so can you or WalletBrake before the seller collects.
- Large payments to a new seller are refused (
x402-needs-delay): the vault would hold them for an hour, and a seller can't wait. Trust the seller first, or pay less. - One transaction per payment. Recording it costs the spending wallet a little gas, unlike x402 from an ordinary wallet.
- New vaults only. Vaults from the current factory (Networks and addresses) can pay x402; older vaults' contracts can't vouch for a payment, so create a new vault to use it.
- Coin. x402 sellers take Circle's USDC. On Base Sepolia the vault shows it as USDC (Circle), with the same limits as USDC; get some from Circle's faucet. The app's free test USDC isn't accepted by sellers.
How it works on chain: x402 sellers want a USDC transfer signed by the payer. A vault is a contract and can't sign, so it vouches instead (ERC-1271): after WalletBrake approves, the vault records the one transfer, and when USDC asks the vault whether it signed that transfer, the vault says yes to that one only.
12A Safe as your owner wallet#
Your vault's owner wallet can be a Safe multisig, so changing or emptying the vault takes several people instead of one key. Create the vault from the Safe: in the app, choose WalletConnect, copy the link, and paste it into Safe{Wallet}'s WalletConnect.
- Owner actions are Safe transactions. Freezing, unfreezing, trusting an address, raising limits and the emergency move are proposed in your Safe. Your signers confirm them in Safe{Wallet}, one of them executes, and the app follows along until it lands.
- Any signer can sign in. Each person behind the Safe can sign in to the vault page with their own wallet, to see activity. Moving money or changing settings still needs the Safe.
- Large payments need the Safe. Approving one in the app takes the Safe's own signature, as many signers as it requires. One signer alone can't approve it.
- Freezing takes the Safe too. A freeze you start waits for your signers. WalletBrake's automatic freezes don't: they happen at once.
- The Safe must exist on every network you use the vault on, at the same address. The app checks before you create the vault there. Your backup address can be a Safe as well.
- Networks. Safe{Wallet} works with Base Sepolia and Ethereum Sepolia. Its contracts are on BNB testnet too, but its app and service aren't.
13Alerts#
WalletBrake tells you when a payment waits, a setting changes, or your vault freezes. Turn alerts on in the app: open the vault, sign in with your owner wallet, and go to Safety → Alerts.
- Telegram. Tap Connect Telegram. Approval questions for payments to untrusted addresses come only here, with Approve and Reject buttons.
- Email. Enter an address and press Send a confirmation email. Alerts start once you press the button in that email, so nobody can sign up someone else's address. The link works for 24 hours. Up to 3 addresses per vault.
- What an email says. Urgent alerts start with
[WalletBrake] Urgent:, so you can filter them. Each email names the vault, the network and the time, and links to the vault page and the transaction. - Stopping them. Every email has an unsubscribe link, and mail apps that support one-click unsubscribe show their own button. You can also remove an address on the vault page.
- Emails can't approve payments. They only inform you. To act, open your vault page and use your owner wallet. WalletBrake never asks for your seed phrase or private keys, by email or anywhere else.
14HTTP API#
For other languages. Each vault lives at https://app.walletbrake.com/c/<chainId>/v/<vault>; use the vault address exactly as the app shows it (mixed case), or the server redirects.
Ask for an approval
Sign this text with the spending wallet's key, as a standard personal_sign (EIP-191) message. Addresses are checksummed, the amount is in base units, the time is Unix seconds within 60 seconds of now, and the request ID is 16 to 64 random characters, used once.
WalletBrake payment request
Vault: 0xVault…
Chain ID: 84532
Token: 0xf886B9E161030C1e7e4375158bFA01e4c65d9999
To: 0xSupplier…
Amount: 10000000
Time: 1793729400
Request ID: 3f9c1a…
{ "token": "0xf886…9999", "to": "0xSupplier…", "amount": "10000000",
"time": 1793729400, "id": "3f9c1a…", "signature": "0x…" }
- 200:
{ vault, token, to, amount, nonce, deadline, signature, calldata, queued }. Check that the vault, token, address and amount are what you asked for. Then send a transaction to the vault from the spending wallet withdata: calldata(it'swithdraw(token, to, amount, nonce, deadline, signature)), beforedeadline. - 403: refused,
{ verdict, rule, reason }(see Refusals). - 401: the signature isn't from the vault's spending wallet, or the time or ID is wrong.
Open a task
Sign this text the same way. The budget is in base units; Pay only is the checksummed addresses joined by ", ", or any address; Open for is 1 to 1440 minutes.
WalletBrake task
Vault: 0xVault…
Chain ID: 84532
Task: Pay the March hosting invoice
Token: 0xf886B9E161030C1e7e4375158bFA01e4c65d9999
Budget: 100000000
Pay only: 0xHostingCompany…
Open for: 60 minutes
Time: 1793729400
Request ID: 7be01d…
{ "description": "Pay the March hosting invoice", "token": "0xf886…9999",
"budget": "100000000", "payOnly": ["0xHostingCompany…"], "minutes": 60,
"time": 1793729400, "id": "7be01d…", "signature": "0x…" }
200: { task, description, token, symbol, budget, spent, payOnly, expires }. To pay in the task, add "task" (and optionally "note") to POST /v1/approve, and add two lines to the end of the signed payment text: Task: t_… and Note: … (empty after the colon if there's no note).
Approve an x402 payment
Sign this text the same way. Valid after and Valid before are the USDC transfer's Unix times (before at most an hour from now), and the authorization nonce is 32 random bytes in lowercase hex. Task, Note and Resource (the paid URL) are always there, empty after the colon if unused.
WalletBrake x402 payment request
Vault: 0xVault…
Chain ID: 84532
Token: 0x036CbD53842c5426634e7929541eC2318f3dCF7e
To: 0xSeller…
Amount: 10000
Valid after: 0
Valid before: 1793729700
Authorization nonce: 0x9c41…
Time: 1793729400
Request ID: 5d0e2a…
Task:
Note:
Resource: https://api.example.com/weather
{ "token": "0x036C…CF7e", "to": "0xSeller…", "amount": "10000",
"validAfter": "0", "validBefore": "1793729700", "authNonce": "0x9c41…",
"resource": "https://api.example.com/weather",
"time": 1793729400, "id": "5d0e2a…", "signature": "0x…" }
200: the approval plus calldata for authorizeX402; send it to the vault from the spending wallet. Then put this in the request's PAYMENT-SIGNATURE header, base64 JSON: x402Version 2, the offer as accepted, and payload with signature: "0x01" and authorization: { from: vault, to, value, validAfter, validBefore, nonce }. Refusals are as for /v1/approve.
Read the vault
GET /v1/public/info: the vault's owner, spending wallet, whether it's frozen, whether it pays only trusted addresses, and per coin its address, decimals, balance, limits, what's left this hour, the hold threshold and the biggest single payment.
15Networks and addresses#
The contracts have the same address on every network, and so does a vault you add to more networks. Their source code is published and verified on each network's block explorer, so you can read exactly what your vault runs.
| Network | Chain ID | Coins |
|---|---|---|
| Base Sepolia | 84532 | USDC (test), USDC (Circle), ETH |
| BNB Smart Chain testnet | 97 | USDC (test), BNB |
| Ethereum Sepolia | 11155111 | USDC (test), ETH |
| Contract | Address | Verified source |
|---|---|---|
| Vault factory | 0xF86e75d2C22dA4CCb5394665A6da83133BaeE78f | Basescan · Etherscan · BscScan |
| Vault core (every vault is a copy of it) | 0x52eE3C2203B4e2011Ba6720d106709809B908455 | Basescan · Etherscan · BscScan |
| Vault module (settings, freeze, rescue) | 0x603d5304104C59D338d13a0FC5370Dc6AfD49B7B | Basescan · Etherscan · BscScan |
| Test USDC (6 decimals) | 0xf886B9E161030C1e7e4375158bFA01e4c65d9999 | Basescan · Etherscan · BscScan |
| USDC (Circle), Base Sepolia only | 0x036CbD53842c5426634e7929541eC2318f3dCF7e | Basescan (Circle's) |
The test USDC has a free faucet in the app (Add 1,000 test USDC). It isn't real USDC. Vaults from the earlier factories (0x8d609B9E…866A, 0xF604e58c…6eED) are still protected.
16Security#
- Beta on test networks. The contracts aren't independently audited yet. Use test money only; main networks come after the audit.
- WalletBrake can't move your money. It can approve, refuse, freeze and cancel a held payment. It can't take anything, keep your vault frozen, block a rescue, or stop you replacing it.
- Your exit doesn't need WalletBrake. If WalletBrake ever goes away, your owner wallet can freeze the vault and move everything to your backup address, using only your wallet and the blockchain: walletbrake.com/recover does it without any WalletBrake server, and you can save a copy. If the owner wallet is lost too, your backup address can start a rescue and, after the wait, everything goes to it. Each of these is proven by tests that remove WalletBrake or turn it hostile.
- Recipients are checked. Before paying an address you haven't trusted, WalletBrake checks it against public sanctions lists (the US Treasury's OFAC list, refreshed every 6 hours), against the addresses your vault has paid (a look-alike, same first and last characters but a different address, is a common trick called address poisoning), and for contracts created in the last few days. A listed address is refused and freezes the vault; the others go to you first.
- Canary addresses. In Safety, make decoy addresses nobody should ever pay, and plant them where only an attacker or a tricked agent would find them. A request to pay one freezes the vault at once, before any other rule. They're never shown on the public vault page.
- Try to break it. Break the Brake lets anyone try to trick a real AI payments agent into paying them from a protected vault (test money), and shows what WalletBrake did.
- Keep the agent's key with the agent. It can only ask for payments, within your limits. Never put it in a prompt, a log, or anything the agent reads back.
- Found a problem? security@walletbrake.com. Questions: support@walletbrake.com.