Source: /docs/introduction
# Introduction
> Aider is a USDC wallet for AI agents on Arc. You keep the money; each agent gets an allowance.
## What Aider does
Aider lets an AI agent, such as Claude Code, pay for things with USDC **without holding your money**.
- **You keep the money.** Your USDC sits in your Aider wallet on Arc. Only you can withdraw it.
- **Each agent gets an allowance.** You choose how much it can pay at once and per day. Payments inside that go through by themselves.
- **Bigger payments wait for you.** The agent asks, and you approve or reject it in the app.
- **You can stop an agent at any time.** One click revokes it.
## How you set it up
1. **In the app:** connect your wallet, create an Aider wallet and add some USDC.
2. **On the agent's computer:** install `aidr` and run `aidr setup`. It prints a link.
3. **Open the link:** choose the agent's limits and click **Add agent**.
That's all. The agent can now pay. Follow the [quickstart](/docs/quickstart) for the exact steps.
## The parts
| Part | Where | What it's for |
|---|---|---|
| The app | [aider-smoky.vercel.app/app](https://aider-smoky.vercel.app/app) | You: add money, add agents, approve, revoke. |
| `aidr` | The agent's computer (`npm install -g aidr-cli`) | The agent: check its budget, pay, wait for approval. |
| `SKILL.md` | [/skill.md](/skill.md) | Instructions that teach an AI agent to use `aidr` safely. |
| The Aider contract | Arc | Holds the money and checks every rule. |
## Testnet and mainnet
Use the **Testnet / Mainnet** switch in the top bar. The app and these docs follow it.
You're reading the **Arc Testnet** version. The USDC is free test money from Circle's faucet, so it's the place to try things.
> [!WARNING]
> Aider is experimental and unaudited. Use small amounts.
## Next
Start with the [quickstart](/docs/quickstart).
---
Source: /docs/quickstart
# Quickstart
> Your first agent payment in 5 steps, about 10 minutes.
## Step 1: Get USDC
1. Install a browser wallet such as MetaMask if you don't have one.
2. Get free test USDC from [faucet.circle.com](https://faucet.circle.com). Choose **Arc Testnet** and paste your wallet address.
On Arc, fees are also paid in USDC, so this one balance covers everything.
## Step 2: Create your Aider wallet
1. Open [the app](https://aider-smoky.vercel.app/app) and click **Connect wallet**. If asked, switch your wallet to Arc Testnet.
2. Click **Create wallet** and confirm.
3. Click **Deposit**, enter `5` and confirm the two prompts (**Approve**, then **Deposit**).
## Step 3: Install aidr on the agent's computer
The agent's computer is wherever the agent runs, for example your laptop if you use Claude Code. Open a terminal and run:
```bash title="Terminal"
npm install -g aidr-cli
aidr --version
```
`aidr --version` should print a version number. If it says `command not found`, close the terminal, open a new one and try again.
## Step 4: Connect the agent
In the same terminal:
```bash title="Terminal"
aidr setup --name "Claude Code"
```
It prints a link like this:
```text title="Output"
Open this link where your Aider wallet is, set the limits and click Add agent:
https://aider-smoky.vercel.app/connect?agent=0x7D47…bebf&name=Claude+Code&chain=5042002&…
```
Open the link in the browser where your wallet is connected. The **Add agent** form is already filled in:
1. Choose the limits, for example **$0.10 per payment** and **$0.50 per day**.
2. Leave **Send 0.05 USDC for gas** on. The agent needs a little USDC to send payments.
3. Click **Add agent** and confirm the two prompts.
Check it worked:
```bash title="Terminal"
aidr balance
```
Look for `"status": "active"`.
## Step 5: Make a payment
```bash title="Terminal"
aidr pay 0x90F79bf6EB2c4f870365E785982E1f101E93b906 0.05 "My first agent payment"
```
You get `"status": "paid"` and an explorer link. The payment also appears in the app under **Activity**.
Try one over the limit:
```bash title="Terminal"
aidr pay 0x90F79bf6EB2c4f870365E785982E1f101E93b906 1 "Bigger purchase"
```
You get `"status": "pending_approval"`. Open **Approvals** in the app and click **Approve**.
> [!TIP]
> You don't have to type these yourself. Start Claude Code and ask it to pay; see [use it with Claude](/docs/use-with-claude).
## Next
- [Use it with Claude](/docs/use-with-claude): what to say, and what agents can pay for.
- [Owner guide](/docs/quickstart-owners): approvals, revoking, pausing and withdrawing.
- [Agent guide](/docs/quickstart-agents): every `aidr` command in plain steps.
---
Source: /docs/quickstart-owners
# Owner guide
> Everything you do in the app, as short steps.
Open [the app](https://aider-smoky.vercel.app/app), check the switch in the top bar says **Testnet**, and connect your wallet. Everything below happens there.
## Add money
1. Go to **Overview** and click **Deposit**.
2. Enter an amount and confirm **Approve**, then **Deposit**. If you approved enough before, there's only one prompt.
## Add an agent
1. On the agent's computer, run `aidr setup --name "Claude Code"` (see the [agent guide](/docs/quickstart-agents)).
2. Open the link it prints. **Add agent** opens with the agent's address and name filled in.
3. Choose its limits:
| Setting | Example | What it means |
|---|---|---|
| Per payment | `$0.10` | The biggest payment it can make without asking you. |
| Per day | `$0.50` | The most it can spend without asking you, per day (resets at 00:00 UTC). |
| Key expires | 30 days | After this the agent stops working until you extend it. |
| Send 0.05 USDC for gas | On | Sends the agent a little USDC for fees. |
4. Click **Add agent** and confirm.
You never handle the agent's key. It stays on the agent's computer.
> [!NOTE]
> No link? Click **Add agent** on the **Agents** page. It shows the `aidr setup` command to run, or you can paste an address.
## Approve or reject a payment
When an agent asks for more than its limit, the money is set aside and the request appears in **Approvals**.
- Click **Approve** to pay it.
- Click **Reject** to say no. The money goes back to your available balance.
If you do nothing, the request expires (after 30 minutes by default) and nothing is paid.
## Change limits
On **Agents**, click **Edit limits**. New limits apply to the next payment.
## Stop an agent
- **One agent:** on **Agents**, click **Revoke**. It can't pay from the next block, and its open requests are cancelled.
- **All agents at once:** on **Overview**, click **Pause all agents**. Click **Resume agents** to undo it.
## Send an agent more gas
On **Agents**, click **Connect this agent**, then **Send 0.05 USDC for gas**.
## See what happened
**Activity** lists every deposit, payment, request and change, with the memo the agent wrote and an explorer link.
## Take your money out
On **Overview**, click **Withdraw**. Only you can do this.
## Next
Choose good limits with [spending rules](/docs/spending-rules), or see the [emergency controls](/docs/revoking-and-pausing) in detail.
---
Source: /docs/quickstart-agents
# Agent guide
> Install aidr, connect the agent and pay, as short steps.
## Install
```bash title="Terminal"
npm install -g aidr-cli
aidr --version
```
To update later, run `npm install -g aidr-cli@latest`.
## Connect to a wallet
```bash title="Terminal"
aidr setup --name "Claude Code"
```
1. `aidr` makes the agent its own key and keeps it on this computer (in `~/.aidr/key`). It never prints it.
2. It prints a link. The wallet owner opens it, chooses the limits and clicks **Add agent**.
3. Run `aidr balance`. The first time, it finds the wallet by itself.
Until the owner adds the agent, commands answer `NotLinked` and show the link again.
## Check the budget
```bash title="Terminal"
aidr balance
```
| Field | What to look at |
|---|---|
| `agent.status` | Must be `active` to pay. |
| `maxDirectPayment` | The biggest payment that goes through right now without approval. |
| `agent.remainingToday` | What's left of today's limit. |
## Pay
```bash title="Terminal"
aidr pay
""
```
```bash title="Example"
aidr pay 0x90F79bf6EB2c4f870365E785982E1f101E93b906 0.05 "Weather API credits"
```
| Result | Meaning |
|---|---|
| `"status": "paid"` | Done. Includes an explorer link. |
| `"status": "pending_approval"` | Over a limit. Not paid yet: the owner has to approve it. Note the `requestId`. |
| `"status": "error"` | Nothing was paid. `message` says why and `hint` says what to do. |
## Wait for approval
```bash title="Terminal"
aidr wait 42 --timeout 2
```
`42` is the `requestId`. It prints the result once the owner approves or rejects, or when the request expires. `aidr status 42` checks once without waiting.
## See past payments
```bash title="Terminal"
aidr history
```
## Remove the agent from this computer
```bash title="Terminal"
aidr reset --yes
```
This deletes the key. The owner should also click **Revoke** in the app.
## Special cases
| Situation | Command |
|---|---|
| The owner runs the app somewhere else (for example locally) | `aidr setup --app http://localhost:5173` |
| You already know the wallet number | `aidr setup --wallet 1` |
| The other network | Testnet: `aidr setup`. Mainnet: `aidr setup --network mainnet`. |
| The owner gave you an existing key | `export AIDR_AGENT_KEY=0x…` then `aidr init --wallet 1` |
## Next
Let Claude run these commands for you: [use it with Claude](/docs/use-with-claude). Every flag is in the [CLI reference](/docs/cli-reference).
---
Source: /docs/use-with-claude
# Use it with Claude
> Let Claude Code pay for you, with example prompts and ideas for what to pay for.
## Set up once
1. Finish the [quickstart](/docs/quickstart) so `aidr balance` shows `"status": "active"`.
2. Start Claude Code in a terminal:
```bash title="Terminal"
claude
```
3. Give it the rules. Paste this as your first message:
```text title="Message to Claude"
Read https://aider-smoky.vercel.app/skill.md. Use the aidr command to pay for things, and follow its rules.
```
## What to say
```text title="Check the budget"
How much can you still spend today? Use aidr balance.
```
```text title="Pay"
Pay 0.05 USDC to 0x90F79bf6EB2c4f870365E785982E1f101E93b906 for "Translation job #1".
```
```text title="A bigger payment"
Pay 1 USDC to 0x90F79bf6EB2c4f870365E785982E1f101E93b906 for "Premium dataset", then wait for my approval.
```
Claude creates an approval request and tells you. Approve it in the app under **Approvals**.
```text title="Check history"
Show me the last 5 payments you made.
```
## What agents can pay for
An agent pays a USDC **address on Arc**. Anything with an address can be paid:
| Idea | Example |
|---|---|
| Another agent | Pay a translation agent 0.05 USDC per job. |
| A person | Pay a 0.50 USDC bounty for a bug fix. |
| A paid API | Pay per request to an API that accepts USDC on Arc. |
| A bigger purchase | Ask for 2 USDC for a dataset and wait for your approval. |
An agent can't pay with a card or check out on a normal website.
## Try the safety controls
1. Ask Claude to pay something small. It goes through.
2. In the app, click **Revoke** on the agent.
3. Ask again. The payment is blocked and Claude tells you why.
## Claude desktop app and ChatGPT
These apps can't run terminal commands, so they can't use `aidr` yet. Use Claude Code, or another agent that can run commands.
## Next
Read the rules Claude follows in [SKILL.md](/docs/skill-md), and good habits in [best practices](/docs/best-practices).
---
Source: /docs/how-it-works
# How it works
> The pieces, where the money lives, and what happens to every payment.
## The pieces
| Piece | Who uses it | Holds |
|---|---|---|
| Owner | You, with a normal browser wallet | Full control of your Aider wallet |
| Aider wallet | Lives inside the Aider contract | A USDC balance, a reserved amount, rules and agents |
| Agent | An AI agent with its own key | Permission to spend within its limits |
| Aider contract | Everyone, onchain on Arc | All wallets' USDC and every rule |
| `aidr` | The agent's machine | The agent key, in `~/.aidr/` |
## Where the money lives
All USDC sits in the Aider contract. Each wallet has its own ledger inside it:
- **balance**: the USDC this wallet holds;
- **reserved**: the part set aside for open approval requests;
- **available**: balance minus reserved, which is what agents can spend and what you can withdraw.
Only the wallet's owner can withdraw. There is no admin key and no upgrade path, so nobody else can move the money out.
## The life of a payment
```text title="aidr pay"
agent runs: aidr pay ""
│
▼
contract checks, in order: key active? not expired? wallet not paused?
valid recipient? not blocked? allowed?
≤ per-payment limit? ≤ what's left today? enough available?
│
├── all pass ──────────────► PAID: USDC goes to , AgentPaid event
│
├── over a limit ──────────► aidr creates an APPROVAL REQUEST:
│ amount reserved, owner approves or rejects
│
└── anything else ─────────► BLOCKED: nothing moves, the error says why
```
## Days are UTC days
Each agent's spending counter resets at 00:00 UTC. A key could therefore spend its daily limit just before midnight and again just after, so the most it can spend inside a short window is up to twice the daily limit. After you revoke it, it can spend nothing.
## What's onchain and what isn't
Every deposit, payment, request, approval, rejection and rule change is an Arc transaction with an event, so anyone can check it on the explorer. The owner app reads those events to show Activity; it doesn't store anything itself. The CLI keeps only its config and key on the agent's machine.
## Next
Go through [agent payments](/docs/agent-payments) check by check, then [approval requests](/docs/approval-requests).
---
Source: /docs/agent-payments
# Agent payments
> What happens when an agent pays, check by check, and what each failure means.
## The call
An agent pays by calling `agentPay(walletId, to, amount, memo)` with its own key. With the CLI that's `aidr pay ""`. Amounts use USDC's 6 decimals, so `0.05` USDC is `50000`.
## The checks, in order
The contract runs these checks one after another and stops at the first that fails. Nothing moves unless every check passes.
| # | Check | Error if it fails |
|---|---|---|
| 1 | The wallet exists | `WALLET_NOT_FOUND` |
| 2 | The caller was added as an agent of this wallet | `AgentNotActive` |
| 3 | The agent hasn't been revoked | `AgentRevokedError` |
| 4 | The agent's expiry hasn't passed | `AgentExpired` |
| 5 | The wallet isn't paused | `WalletPaused` |
| 6 | The recipient isn't the zero address | `ZeroAddress` |
| 7 | The recipient isn't the Aider contract or the USDC token (v2) | `InvalidRecipient` |
| 8 | The amount is more than zero | `ZeroAmount` |
| 9 | The recipient isn't on the blocklist | `RecipientBlocked` |
| 10 | In allowlist mode, the recipient is on the allowlist | `RecipientNotAllowed` |
| 11 | The amount is at most the per-payment limit | `OverPerPaymentLimit` |
| 12 | Spent today plus the amount is at most the daily limit | `OverDailyLimit` |
| 13 | The wallet's available balance covers it | `InsufficientBalance` |
Before check 12 the contract resets the agent's counter if a new UTC day has started.
## When every check passes
1. The amount is added to the agent's spent-today counter.
2. The wallet's balance goes down by the amount.
3. The contract sends the USDC to the recipient.
4. It emits `AgentPaid(walletId, agent, to, amount, memo)`.
The payment is final as soon as the transaction confirms.
## When a limit is in the way
If check 11 or 12 fails, `aidr pay` doesn't give up: it creates an [approval request](/docs/approval-requests) for the same payment, so the owner can decide. Use `--no-request` to get the error instead.
Every other failure is returned as an error and no request is created. In particular `InsufficientBalance` never turns into a request, because a request would need the same available balance.
## Memos
Every payment carries a memo. The owner sees it in Activity and on approval cards, so write what the payment is actually for: `API credits for the weather lookup`, not `payment`. The CLI requires a memo and limits it to 256 bytes.
## Never pay the contract itself
Version 2 of the contract rejects payments to the Aider contract's own address or to the USDC token with `InvalidRecipient`, and the CLI refuses them too. USDC sent to either would be stuck. To pay another agent, pay that agent's own address; see [agent-to-agent payments](/docs/agent-to-agent).
## Next
See what happens to over-limit payments in [approval requests](/docs/approval-requests).
---
Source: /docs/approval-requests
# Approval requests
> How payments over an agent's limits wait for the owner, with the money reserved.
## When a request is created
An agent calls `requestPayment(walletId, to, amount, memo, ttlSeconds)` when it needs to pay more than its limits allow. The CLI does this for you when `aidr pay` hits the per-payment or the daily limit, with a default TTL (time to decide) of 30 minutes; `--ttl` changes it.
The same checks as a payment apply first: the agent must be active and unexpired, the wallet unpaused, and the recipient valid and allowed. Then:
- the amount moves from **available** to **reserved**, so nothing else can spend it;
- the request gets an id and the event `PaymentRequested(requestId, walletId, agent, to, amount, memo, expiresAt)`;
- it shows up in the owner's **Approvals** inbox.
In version 2 the TTL must be between 1 second and 7 days (`InvalidTTL` otherwise), and an agent can have at most 10 open requests per wallet (`TooManyOpenRequests`).
## Approve
Only the wallet's owner can approve, and only while:
- the TTL hasn't passed (`RequestExpiredError` after it);
- the agent that made the request is still active and unexpired (v2: `AgentRevokedError` or `AgentExpired`);
- the recipient is still allowed by the current rules (`RecipientBlocked`, `RecipientNotAllowed`).
Approving pays the recipient from the reserved money and emits `RequestApproved`. Approved payments **don't count** toward the agent's daily limit. Approvals still work while the wallet is paused, because pausing only stops agents.
## Reject
The owner can reject a pending request at any time. The reserved amount goes back to available and `RequestRejected` is emitted. Version 2 adds `rejectRequests(ids)` to reject many in one transaction, for example after revoking an agent.
## Expire and release
A request whose TTL has passed can't be approved, but its money stays reserved until someone calls `expireRequest(id)`. Anyone can call it. It releases the reservation and emits `RequestExpired`. Version 2 also lets anyone release a request at once if the agent that made it was revoked or has expired, and adds `expireRequests(ids)` for many at once.
## Statuses
| Status | Meaning |
|---|---|
| Pending | Waiting for the owner. Money reserved. |
| Approved | Paid. |
| Rejected | The owner said no. Money released. |
| Expired | Released after its TTL, or voided because its agent was revoked or expired. |
A request can still be stored as pending after its TTL passed, if nobody has released it yet. The app shows it under "Expired" with a **Release** button, and `aidr status` reports it as `expired`.
## From the agent's side
```bash title="Terminal"
aidr status 42 # pending, approved, rejected or expired
aidr wait 42 --timeout 1.5 # polls every 5 seconds until it's decided
```
## Next
See all the [spending rules](/docs/spending-rules) that decide when a request is needed.
---
Source: /docs/spending-rules
# Spending rules
> Every rule an owner can set, what it does and where to change it.
## Per agent
| Rule | Type | Example | What it does | Change it with |
|---|---|---|---|---|
| Per-payment limit | USDC | `$0.10` | The largest single payment the agent can make on its own. | Agents → Edit limits (`updateAgent`) |
| Daily limit | USDC | `$0.50` | The most the agent can spend on its own per UTC day. | Agents → Edit limits (`updateAgent`) |
| Expiry | date or never | 30 days | After this the key stops working. | Agents → Edit limits (`updateAgent`) |
| Label | text | `Claude Code` | A name for Activity and approval cards. | Re-add the agent (`addAgent`) |
Version 2 rejects an expiry that is already in the past (`InvalidExpiry`); "never" is stored as `0`.
Anything over the per-payment or daily limit can still happen, but only as an [approval request](/docs/approval-requests) you approve.
## Per wallet
| Rule | Values | What it does | Change it with |
|---|---|---|---|
| Recipient mode | Anyone / Allowlist only | Who agents may pay. | Rules (`setRecipientMode`) |
| Allowlist | addresses | In allowlist mode, the only recipients allowed. | Rules (`setAllowlist`) |
| Blocklist | addresses | Recipients that can never be paid, in any mode. | Rules (`setBlocklist`) |
| Paused | on / off | Stops every agent of the wallet. | Overview (`pauseWallet`) |
A blocked address always loses: it can't be paid even if it's also on the allowlist. Recipient rules apply to payments, to new requests, and again when you approve a request.
## Built in
| Rule | Value |
|---|---|
| Longest request TTL | 7 days (v2) |
| Open requests per agent per wallet | 10 (v2) |
| Recipients that are always refused | The Aider contract and the USDC token (v2) |
## The daily limit and UTC days
The spent-today counter resets at 00:00 UTC, not 24 hours after the first payment. Re-adding an agent the same day keeps what it already spent (v2), so re-enabling a revoked agent doesn't give it a fresh allowance. Because the reset is at midnight UTC, a key can spend up to twice its daily limit in a short window around midnight. Size the daily limit with that in mind.
## Next
Learn the emergency controls in [revoking and pausing](/docs/revoking-and-pausing).
---
Source: /docs/revoking-and-pausing
# Revoking and pausing
> The two emergency controls, what each one stops, and how to undo them.
## Revoke or pause?
| | Revoke an agent | Pause the wallet |
|---|---|---|
| Stops | One agent | Every agent of the wallet |
| Its direct payments | Rejected (`AgentRevokedError`) | Rejected (`WalletPaused`) |
| Its new requests | Rejected | Rejected |
| Its open requests | Voided in v2: can't be approved, anyone can release them | Still there; you can approve or reject them |
| Owner actions | All still work | All still work, including withdraw and approve |
| Undo | Re-add the agent | Resume |
Use **revoke** when one agent misbehaves or its key may have leaked. Use **pause** when you want everything to stop while you look.
## Revoke
In the app, open **Agents** and click **Revoke** on the agent. The contract call is `revokeAgent(walletId, agent)`. From the next block the agent can't pay or create requests.
In version 2, revoking also voids the agent's open requests. They can never be approved, even if you add the agent again later, and anyone can release their reserved money with `expireRequest`. The app shows them with a **Release** button. On version 1, those requests stay approvable, so the app only offers **Reject** for requests from revoked agents.
## Re-enable
Click **Re-enable** on a revoked agent. This calls `addAgent` again with the same label and limits. In version 2 the agent keeps what it already spent today, so re-enabling doesn't reset its daily limit.
## Pause
On **Overview**, click **Pause all agents**. The call is `pauseWallet(walletId, true)`. Every agent's payments and requests are rejected with `WalletPaused` until you click **Resume**. You can still deposit, withdraw, change rules and decide on requests while paused.
## Expiry
An agent whose expiry has passed behaves like a revoked one (`AgentExpired`) until you extend it with **Edit limits**. In version 2 its open requests can't be approved and can be released by anyone.
## Next
See the full picture in the [security model](/docs/security-model).
---
Source: /docs/agent-to-agent
# Agent-to-agent payments
> How one agent pays another, and what each side should check.
## What it means here
An agent pays another agent the same way it pays anyone: with `aidr pay` to that agent's **address**. The payment comes out of the paying agent's Aider wallet, inside its limits, and lands as USDC at the other agent's address.
```bash title="Agent A pays agent B for a summary"
aidr pay 0x3C44…93BC 0.08 "Summary of the Q3 report (job 17)"
```
If agent B also has an Aider wallet, its owner can deposit what it earns into that wallet later. There is no direct wallet-to-wallet transfer inside the contract.
## Both sides keep their own rules
- Agent A's payment must pass A's wallet rules: its per-payment and daily limits, recipient rules and pause switch. If B's address isn't allowed, A gets `RecipientNotAllowed` or `RecipientBlocked`.
- Agent B's own spending is limited by B's wallet, if it has one. Receiving money doesn't raise B's limits.
## Memos that both sides can match
Put something both agents know in the memo, such as a job id: `Summary of the Q3 report (job 17)`. The memo is stored in the `AgentPaid` event, so B, or B's owner, can find the payment on the explorer.
## Checking that you were paid
The receiving agent can look up the payment by transaction hash on the explorer, or watch the `AgentPaid` events for its address; see [watching activity](/docs/watching-activity). `aidr history` lists payments an agent *made*, not ones it received.
## Don't pay the contract
Never pay the Aider contract's address thinking it is "agent B's wallet". Version 2 rejects that payment with `InvalidRecipient` and the CLI refuses it, because USDC sent to the contract that way would be stuck.
## Next
See how to follow every payment in [watching activity](/docs/watching-activity).
---
Source: /docs/watching-activity
# Watching activity
> Every event the contract emits, and how to follow them in the app or your own code.
## Events
Everything that changes a wallet emits an event. Indexed fields can be used as filters.
| Event | Fields (indexed in bold) | When |
|---|---|---|
| `WalletCreated` | **walletId**, **owner** | A wallet is created |
| `Deposited` | **walletId**, amount | The owner deposits |
| `Withdrawn` | **walletId**, amount | The owner withdraws |
| `WalletPausedEvent` | **walletId**, paused | Pause or resume |
| `AgentAdded` | **walletId**, **agent**, label | An agent is added or re-added |
| `AgentUpdated` | **walletId**, **agent** | Limits or expiry changed |
| `AgentRevoked` | **walletId**, **agent** | An agent is revoked |
| `AgentPaid` | **walletId**, **agent**, **to**, amount, memo | A direct payment |
| `PaymentRequested` | **requestId**, **walletId**, **agent**, to, amount, memo, expiresAt | A request is created |
| `RequestApproved` | **requestId**, **walletId** | A request is approved and paid |
| `RequestRejected` | **requestId**, **walletId** | A request is rejected |
| `RequestExpired` | **requestId**, **walletId** | A request is released |
| `RecipientModeSet` | **walletId**, mode | Anyone / allowlist only |
| `AllowlistUpdated` | **walletId**, recipient, allowed | Allowlist change |
| `BlocklistUpdated` | **walletId**, recipient, blocked | Blocklist change |
## In the app
**Activity** lists every event for your wallet with the memo, amounts and an explorer link, and filters by type and agent. The app reads the events straight from Arc in windows of up to 10,000 blocks (the RPC's limit) and caches what it has read in your browser.
## In your own code
A short script with [viem](https://viem.sh) that lists one wallet's direct payments:
```ts title="payments.ts"
import { createPublicClient, http, parseAbiItem } from 'viem'
import { arcTestnet } from 'viem/chains'
const client = createPublicClient({ chain: arcTestnet, transport: http() })
const latest = await client.getBlockNumber()
const logs = await client.getLogs({
address: '0xed32e15d81e7864bf19caea4333bd0781f328ba3',
event: parseAbiItem('event AgentPaid(uint256 indexed walletId, address indexed agent, address indexed to, uint256 amount, string memo)'),
args: { walletId: 1n },
fromBlock: latest - 9_999n, // the RPC allows at most 10,000 blocks per call
toBlock: latest,
})
for (const log of logs) console.log(log.args.to, log.args.amount, log.args.memo)
```
To follow events live, use `watchContractEvent` with the same `address`, `event` and `args`.
## On the explorer
Every transaction has an explorer link in the app and in the CLI output. On Arc Testnet the explorer is [explorer.testnet.arc.io](https://explorer.testnet.arc.io).
## Next
Read the [security model](/docs/security-model).
---
Source: /docs/security-model
# Security model
> Who can do what, what a leaked agent key can and can't do, and where keys live.
> [!WARNING]
> Aider is experimental and has not been audited. Use small amounts.
## Who can do what
| Role | Can |
|---|---|
| Wallet owner | Deposit, withdraw, add/update/revoke agents, set recipient rules, pause, approve and reject requests. |
| Agent | Pay within its limits and create approval requests. Nothing else. |
| Anyone | Read everything, and release requests that can no longer be approved (`expireRequest`). |
| Aider team | Nothing. The contract has no admin, no owner role and no upgrade path. |
## A leaked agent key
| A leaked agent key CAN | A leaked agent key CAN'T |
|---|---|
| Spend up to the agent's daily limit per UTC day (up to twice that across UTC midnight) until you revoke it | Withdraw your funds |
| Make single payments only up to the per-payment limit | Raise its own limits, change its expiry or add agents |
| Create up to 10 open requests (v2) that reserve money until you reject or release them | Approve its own requests |
| Pay only recipients your rules allow | Spend anything after you revoke it or pause the wallet |
| | Pay the Aider contract or the USDC token (v2) |
So the most you can lose to a leaked agent key is roughly its daily limit for each UTC day it stays active. Revoke it as soon as you notice.
## The owner key
Your owner key controls everything in your wallet. Keep it in a browser or hardware wallet, never on an agent's machine. Version 2 of the contract refuses to add the owner's address as an agent (`OwnerCannotBeAgent`), and the CLI refuses the owner's key in `aidr init`.
## Where the agent key lives
`aidr setup` creates the agent's key on the agent's machine, so it never passes through the owner, the app or a chat. It stores it in `~/.aidr/key` and its settings in `~/.aidr/config.json`. The folder is created with mode 700 and the files with mode 600, so only that user can read them. The CLI never prints the key and redacts it from every output.
If you import an existing key with `aidr init`, pass it with the `AIDR_AGENT_KEY` environment variable rather than `--key`, so it doesn't end up in your shell history.
## What the contract guarantees
- Every payment is checked against every rule in the same transaction that pays it.
- Reserved money can't be withdrawn or spent until its request is decided or released.
- The sum of all wallet balances always equals the USDC the contract holds, because the only ways in and out are `deposit`, `withdraw`, `agentPay` and approved requests (v2 refuses payments to the contract itself, which would otherwise strand funds).
## Next
See every failure and what to do about it in [errors](/docs/errors).
---
Source: /docs/errors
# Errors
> Every contract and CLI error, when it happens and what to do.
## Contract errors
These come from the Aider contract. The CLI shows them in `error` and the app shows a plain-English message. Errors marked v2 exist only in version 2 of the contract.
| Error | When | What to do |
|---|---|---|
| `NotWalletOwner` | Someone other than the owner called an owner function | Use the owner's wallet. |
| `WalletPaused` | An agent paid or requested while the wallet is paused | Agent: stop and tell the user. Owner: resume when ready. |
| `AgentNotActive` | The key was never added as an agent of this wallet | Owner: add the agent's address. |
| `AgentRevokedError` | The agent was revoked (also when approving a voided request in v2) | Agent: stop. Owner: re-enable only if you trust it. |
| `AgentExpired` | The agent's expiry passed | Owner: extend it with Edit limits. |
| `ZeroAddress` | The recipient or agent address is `0x0…0` | Use a real address. |
| `InvalidRecipient` (v2) | The recipient is the Aider contract or the USDC token | Pay the recipient's own address. |
| `ZeroAmount` | The amount is 0 | Use a positive amount. |
| `RecipientBlocked` | The recipient is on the blocklist | Don't pay it. |
| `RecipientNotAllowed` | Allowlist mode and the recipient isn't on it | Owner: allowlist it, if you want to. |
| `OverPerPaymentLimit` | The amount is over the per-payment limit | The CLI turns this into an approval request. |
| `OverDailyLimit` | Today's spending would go over the daily limit | The CLI turns this into an approval request. |
| `InsufficientBalance` | The wallet's available balance is too low | Owner: deposit. No request is created. |
| `InvalidTTL` | A request's TTL is 0 (or over 7 days in v2) | Use 1 minute to 7 days. |
| `TooManyOpenRequests` (v2) | The agent already has 10 open requests on this wallet | Owner: decide on some first. |
| `RequestNotFound` (v2) | No request with that id | Use the id from `aidr pay`. |
| `RequestNotPending` | The request was already approved, rejected or released | Nothing to do. |
| `RequestExpiredError` | Approving after the request's TTL | Release it; the agent can ask again. |
| `RequestNotExpiredYet` | Releasing a request that can still be approved | Wait for the TTL, or reject it as the owner. |
| `OwnerCannotBeAgent` (v2) | Adding the owner's own address as an agent | Use a separate key for the agent. |
| `InvalidExpiry` (v2) | An expiry date already in the past | Pick a future date, or "never". |
| `WALLET_NOT_FOUND` | The wallet id doesn't exist | Check the id. |
| `USDC_TRANSFER_FAILED`, `USDC_TRANSFER_FROM_FAILED` | The USDC token refused a transfer | Check balances and the USDC approval. |
## CLI errors
These come from `aidr` itself; nothing reached the contract.
| Error | When | What to do |
|---|---|---|
| `InvalidAmount` | The amount isn't a positive number with at most 6 decimals | Use `0.05`, `1` or `'$0.05'`. |
| `MemoRequired`, `MemoTooLong` | No memo, or more than 256 bytes | Write a short, honest memo. |
| `InvalidAddress` | The recipient isn't a valid address | Check it. |
| `UsageError` | Wrong arguments, for example an unquoted memo | Quote the memo. |
| `Timeout` | `aidr wait --timeout` ran out | Wait again later; don't create a duplicate request. |
| `InsufficientGasFunds` | The agent's address has no USDC for gas | Send it a little USDC. |
| `NonceConflict` | Two transactions from the same key at once | Run one payment at a time. |
| `RpcError`, `ReceiptTimeout` | Network trouble, or no confirmation in time | Check the explorer link or `aidr history` before retrying. |
| `NotLinked` | The owner hasn't added this agent yet | Open the link `aidr setup` printed (it's in `hint`) and click Add agent. |
| `AmbiguousWallet` | The agent was added to more than one wallet | Run `aidr setup --wallet ` to pick one. |
| `NotConfigured`, `KeyMissing`, `KeyMismatch`, `ConfigInvalid` | `aidr` isn't set up on this machine | Run `aidr setup`. |
## Next
Avoid most of these with the [best practices](/docs/best-practices).
---
Source: /docs/best-practices
# Best practices
> Habits that keep agent spending small, visible and easy to stop.
## For owners
- **Start small.** A per-payment limit of `$0.10` and a daily limit of `$0.50` are plenty to try an agent. Raise them once you trust it.
- **One key per agent.** If one leaks you revoke one agent, not all of them.
- **Give keys an expiry.** 7 or 30 days is a good default. You can always extend.
- **Use the allowlist for agents that only pay a few known services.**
- **Keep the wallet lean.** Agents can only spend what's available, so withdraw what you don't need.
- **Revoke agents you no longer use.** Then release their old requests.
- **Read the memos.** Activity shows why every payment was made.
- **Never use your owner key as an agent key.**
## For agents
- **Check `aidr balance` before paying**, and look at `maxDirectPayment`.
- **Write honest memos** that say what the payment is for.
- **One payment at a time.** Payments run one after another onchain, so a parallel payment can fail or turn into an approval request even though your balance check said it fit.
- **Check `aidr history` before retrying** a payment that timed out, so you don't pay twice.
- **Never split a payment to get under a limit.** If it needs approval, let it become a request.
- **Don't treat a request as paid** until `aidr status` says `approved`.
- **Stop when the wallet is paused or your key is revoked or expired**, and tell the user.
- **Never pay the Aider contract address.** Pay the recipient's own address.
## For both
Keep amounts small while Aider is unaudited.
## Next
Look up exact commands in the [CLI reference](/docs/cli-reference).
---
Source: /docs/cli-reference
# CLI reference
> Every aidr command, flag and JSON result.
## Conventions
- Every command prints exactly one JSON object on stdout and a short human line on stderr.
- Exit code `0` means success, `1` means an error. Errors look like `{ "status": "error", "error", "message", "hint" }`.
- Amounts in JSON are USDC decimal strings such as `"0.05"`.
- Config lives in `~/.aidr/config.json` and the key in `~/.aidr/key`.
## aidr setup
Start here. Makes this agent's own key (or keeps the one already saved), saves the config and prints a link the owner opens to add the agent.
```bash title="Terminal"
aidr setup --name "Claude Code"
```
| Flag | Default | Meaning |
|---|---|---|
| `--name