Metadata-Version: 2.5
Name: walletbrake
Version: 0.1.0
Summary: Pay from a WalletBrake vault from a bot or AI agent: spending limits, a trusted-address list and owner approval on Telegram, enforced on chain.
Project-URL: Homepage, https://walletbrake.com
Project-URL: Documentation, https://walletbrake.com/docs#python
Author-email: WalletBrake <support@walletbrake.com>
License-Expression: MIT
License-File: LICENSE
Keywords: agent-payments,ai-agent,base,ethereum,prompt-injection,spending-limits,usdc,wallet,walletbrake,web3
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Office/Business :: Financial
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: requests>=2.31
Requires-Dist: web3<9,>=7
Description-Content-Type: text/markdown

# walletbrake (Python)

Pay from a [WalletBrake](https://walletbrake.com) vault from a bot or AI agent, in Python. It works with LangChain, CrewAI, or plain scripts.

WalletBrake checks every payment against the vault's rules: limits, a trusted-address list, and asking the owner on Telegram. The vault contract enforces the result on chain. A payment needs WalletBrake's signature and the spending wallet's transaction, so neither one can move money alone. There's no API key: each request is signed with the spending wallet's key.

> Test networks only for now (Base Sepolia, BNB testnet, Ethereum Sepolia). Mainnet comes after the security audit.

## Install

```sh
pip install walletbrake
```

Python 3.10 or newer; web3 7 or 8.

## Pay

```python
import os
from walletbrake import WalletBrake

wb = WalletBrake(
    url="https://app.walletbrake.com/c/84532/v/0xYourVault",  # the vault's WalletBrake address
    private_key=os.environ["AGENT_KEY"],                      # the vault's spending wallet
    rpc_url="https://sepolia.base.org",
)
paid = wb.pay("0x…", "10")  # 10 USDC
print(paid.tx_hash)
```

- `amount` is in coin units, as a string or `Decimal` (`"10.5"`). Ints and floats are refused, because `10` could mean 10 USDC or 10 base units, and floats lose cents. For base units, use `amount_base_units=10_500_000`.
- `token` is a symbol (`"USDC"`, `"ETH"`) or a token address. The default is `"USDC"`.

## When the owner has to approve

On the **AI agent** plan, the vault pays only trusted addresses: the ones listed when the vault was created, or added later from the Safety tab. A payment to any other address goes to the owner on Telegram, with Approve and Reject buttons. Until they answer, `pay` raises `DeniedError` and `is_awaiting_approval(err)` returns true. Asking again with the same coin, address and amount goes through once they tap Approve. `pay_and_wait` does the asking again for you:

```python
paid = wb.pay_and_wait("0x…", "10", on_waiting=lambda _: print("Asked the owner on Telegram…"))
```

Each approval covers one payment, for 15 minutes. It doesn't make the address trusted.

## Tasks

Before an agent starts a job, before it reads any web page, email or file,
tell WalletBrake what the job is. Payments in that task can't go past it,
even to addresses you trust:

```python
task = wb.open_task(
    "Pay the March hosting invoice",
    "100",                            # USDC, for the whole task
    pay_only=["0xHostingCompany…"],   # optional
    minutes=60,                       # optional, up to 1440
)
wb.pay_and_wait("0xHostingCompany…", "60", task=task, note="invoice 1042")
```

A payment over what's left of the budget, to an address the task didn't
list, or after the task ends, is refused with rule `"task"`. When the
server's intent check is on, a payment that doesn't clearly fit the task's
description goes to the owner on Telegram first, with the reason.

## Refusals

```python
from walletbrake import DeniedError, WalletBrakeError

try:
    wb.pay(to, "5000")
except DeniedError as err:
    print(err.rule, err.reason)  # e.g. "per-tx-cap", why
```

`err.verdict == "freeze"` means the request looked like an attack and WalletBrake froze the vault. Only the owner can unfreeze it. Other problems raise:
- `WalletBrakeError`: unreachable, wrong key (`status` 401)
- `VaultError`: the vault contract refused, e.g. `NotReady`

## Held payments

The vault holds a large payment to an address it doesn't trust for an hour, so the owner can cancel it. In that case `pay` returns `queued=True` and a `queue_id`. Run it once the hour has passed:

```python
if paid.queued:
    wb.execute_queued(paid.queue_id)
```

## Also

- `wb.info()`: the vault's coins, balances, limits, and whether it's frozen.
- `wb.request_approval(...)` then `wb.send(approval)`: the two halves of `pay`. Before sending, the SDK checks that WalletBrake's approval matches the payment you asked for.

## Docs

Quickstart, every refusal reason, the networks and addresses, and the HTTP API for other languages: **https://walletbrake.com/docs**

Questions: support@walletbrake.com. Security issues: security@walletbrake.com.

## License

MIT
