DEV Community

Cover image for APPROVE_AMOUNT_LIMIT: Cap Your AI Agent's Token Approval Risks
Wallet Guy
Wallet Guy

Posted on

APPROVE_AMOUNT_LIMIT: Cap Your AI Agent's Token Approval Risks

APPROVE_AMOUNT_LIMIT: Cap Your AI Agent's Token Approval Risks

Giving an AI agent a wallet without guardrails is like giving a toddler a credit card — technically functional, practically terrifying. Token approvals are one of the most dangerous operations in crypto: a single unlimited approve() call can hand a third-party contract the right to drain your entire token balance, forever. When an AI agent is the one making those calls autonomously, the stakes are even higher.

Why Token Approvals Are a Specific, Serious Risk

Most developers focus on transfer limits when thinking about agent security. That's the right instinct, but token approvals deserve their own threat model.

When your agent calls approve(spender, amount) on an ERC-20 contract, it's not spending tokens — it's granting permission to spend tokens. That permission persists until explicitly revoked. If your agent approves an unlimited amount (type(uint256).max, the standard DeFi pattern), you've handed a protocol an open-ended claim on that token forever. If the protocol is later exploited, upgraded maliciously, or your agent is compromised and approves a bad address, you have no backstop.

The approval attack surface looks like this:

  • Unlimited approvals — standard in DeFi but gives spenders permanent, uncapped access
  • Approval to wrong addresses — a hallucinated or manipulated contract address
  • Approval storms — an agent in a loop issuing hundreds of small approvals
  • Approval phishing via prompt injection — a malicious input that tricks your agent into approving an attacker's address

These aren't hypothetical. They're documented attack vectors. The question is whether your infrastructure has specific, enforceable controls for them — not just a general "be careful" posture.

How WAIaaS Addresses This Specifically

WAIaaS is an open-source, self-hosted Wallet-as-a-Service built for AI agents. It handles the wallet infrastructure — signing, key management, transaction routing — and wraps every transaction in a policy engine before anything hits the chain.

The policy engine supports 21 policy types. Two of them are directly relevant to approval risk:

APPROVE_AMOUNT_LIMIT caps the maximum amount an agent can approve in a single transaction and optionally blocks unlimited approvals entirely. If your agent tries to issue approve(spender, type(uint256).max), this policy can block it.

APPROVED_SPENDERS is a default-deny whitelist of addresses your agent is allowed to grant approvals to. If the address isn't on the list, the approval doesn't go through — regardless of the amount.

Together, these two policies close the most common approval attack vectors at the infrastructure level, before your agent code runs, before the transaction is signed.

The 3-Layer Security Model

Understanding where APPROVE_AMOUNT_LIMIT sits in the overall security architecture matters. WAIaaS implements a 3-layer security model:

  1. Session authentication → transaction validation — Every API call from your agent requires a session token (JWT HS256). The token is scoped to a specific wallet. There's no way to call the API anonymously.

  2. Policy engine → time delay and approval gates — Every transaction passes through a 7-stage pipeline. Stage 3 is policy evaluation. If a transaction violates any policy, it's denied with a structured error before it reaches the signing stage. Policies support four enforcement tiers: INSTANT, NOTIFY, DELAY, and APPROVAL. You can configure approvals above a certain size to require explicit human sign-off via WalletConnect or Telegram before they execute.

  3. Monitoring + kill switch — The owner (the human who holds the wallet's signing keys) can monitor incoming and outgoing transactions in real time and revoke agent sessions at any moment.

APPROVE_AMOUNT_LIMIT lives in layer 2. It's enforced server-side, in your self-hosted daemon, before the transaction touches a mempool.

Setting Up Approval Guardrails

Here's what a production-grade approval policy stack looks like in practice.

Step 1: Cap approval amounts

Create an APPROVE_AMOUNT_LIMIT policy on your wallet:

curl -X POST http://127.0.0.1:3100/v1/policies \
  -H "Content-Type: application/json" \
  -H "X-Master-Password: my-secret-password" \
  -d '{
    "walletId": "<wallet-uuid>",
    "type": "APPROVE_AMOUNT_LIMIT",
    "rules": {
      "max_approve_usd": 1000,
      "block_unlimited": true
    }
  }'
Enter fullscreen mode Exit fullscreen mode

block_unlimited: true is the important flag here. It means any approval for type(uint256).max — the standard unlimited approval pattern — is rejected outright. Your agent can only issue bounded approvals.

Step 2: Whitelist approved spenders

Create an APPROVED_SPENDERS policy to lock down which addresses can receive approvals:

curl -X POST http://127.0.0.1:3100/v1/policies \
  -H "Content-Type: application/json" \
  -H "X-Master-Password: my-secret-password" \
  -d '{
    "walletId": "<wallet-uuid>",
    "type": "APPROVED_SPENDERS",
    "rules": {
      "spenders": [
        {
          "address": "0xDEF1...",
          "name": "Uniswap Router",
          "maxAmount": "1000000000"
        }
      ]
    }
  }'
Enter fullscreen mode Exit fullscreen mode

This is default-deny: if a spender address isn't in this list, the approval transaction is blocked entirely. Even if your agent generates a valid, well-formed approval transaction for a legitimate-looking address, the policy engine rejects it.

Step 3: Force APPROVAL tier for large approvals

Use APPROVE_TIER_OVERRIDE to force any approval above a threshold to require human sign-off:

curl -X POST http://127.0.0.1:3100/v1/policies \
  -H "Content-Type: application/json" \
  -H "X-Master-Password: my-secret-password" \
  -d '{
    "walletId": "<wallet-uuid>",
    "type": "APPROVE_TIER_OVERRIDE",
    "rules": {
      "min_usd": 500,
      "tier": "APPROVAL"
    }
  }'
Enter fullscreen mode Exit fullscreen mode

Anything above $500 USD equivalent now requires you to manually approve via WalletConnect or Telegram before it executes. The transaction sits in a pending queue until you act on it.

Step 4: Simulate before you commit

Before deploying your agent to mainnet, use the dry-run API to verify that your policies are working as expected:

curl -X POST http://127.0.0.1:3100/v1/transactions/send \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer wai_sess_<token>" \
  -d '{
    "type": "APPROVE",
    "spender": "0xDEF1...",
    "amount": "999999999999999999",
    "dryRun": true
  }'
Enter fullscreen mode Exit fullscreen mode

With dryRun: true, the transaction goes through the full pipeline — validation, auth, policy evaluation — but never gets signed or broadcast. If your APPROVE_AMOUNT_LIMIT policy is configured correctly, this should return a POLICY_DENIED error. That's the response you want to see in testing.

The error format is structured and machine-readable:

{
  "error": {
    "code": "POLICY_DENIED",
    "message": "Transaction denied by APPROVE_AMOUNT_LIMIT policy",
    "domain": "POLICY",
    "retryable": false
  }
}
Enter fullscreen mode Exit fullscreen mode

Your agent code can catch POLICY_DENIED errors specifically and handle them gracefully — log them, alert the operator, or pause execution — rather than failing silently or retrying indefinitely.

The Default-Deny Foundation

It's worth being explicit about what "default-deny" means in practice, because it's not a common design choice in wallet SDKs.

In WAIaaS, if you haven't configured an ALLOWED_TOKENS policy, token transfers are blocked. If you haven't configured a CONTRACT_WHITELIST policy, contract calls are blocked. If you haven't configured APPROVED_SPENDERS, approvals are blocked.

Your agent starts with no permissions and you grant them explicitly. This is the opposite of the common approach where everything is allowed until you explicitly restrict it. It means the blast radius of a compromised agent, a prompt injection attack, or a hallucination is bounded by what you've explicitly permitted — not by what you happened to think to block.

The full list of 21 policy types covers the complete surface area of what an agent might do:

SPENDING_LIMIT          — Amount-based 4-tier security
WHITELIST               — Allowed recipient addresses
TIME_RESTRICTION        — Allowed transaction hours
RATE_LIMIT              — Max transactions per period
ALLOWED_TOKENS          — Token transfer whitelist (default-deny)
CONTRACT_WHITELIST      — Contract call whitelist (default-deny)
METHOD_WHITELIST        — Allowed function selectors
APPROVED_SPENDERS       — Token approval whitelist (default-deny)
APPROVE_AMOUNT_LIMIT    — Max approve amount, block unlimited
APPROVE_TIER_OVERRIDE   — Force tier for APPROVE transactions
ALLOWED_NETWORKS        — Network restriction
X402_ALLOWED_DOMAINS    — x402 payment domain whitelist
LENDING_LTV_LIMIT       — Max loan-to-value ratio for DeFi lending
LENDING_ASSET_WHITELIST — Allowed lending assets
PERP_MAX_LEVERAGE       — Max perpetual futures leverage
PERP_MAX_POSITION_USD   — Max position size in USD
PERP_ALLOWED_MARKETS    — Allowed perpetual markets
REPUTATION_THRESHOLD    — ERC-8004 onchain reputation threshold
ERC8128_ALLOWED_DOMAINS — ERC-8128 HTTP signing domains
VENUE_WHITELIST         — Allowed trading venues
ACTION_CATEGORY_LIMIT   — DeFi action category limits
Enter fullscreen mode Exit fullscreen mode

Each one is a separate, independently configurable guardrail. You can enable just the ones relevant to what your agent actually does.

Quick Start: Approval-Safe Agent in 5 Steps

If you want to get a wallet running with approval guardrails in place, here's the minimal path:

# 1. Install the CLI
npm install -g @waiaas/cli

# 2. Initialize and start the daemon
waiaas init
waiaas start

# 3. Create a wallet and sessions
waiaas quickset --mode mainnet
Enter fullscreen mode Exit fullscreen mode

Then configure your approval policies via the REST API (see examples above), or use the Admin Web UI at http://127.0.0.1:3100/admin, which includes a visual policy editor.

# 4. Explore the interactive API reference
open http://127.0.0.1:3100/reference
Enter fullscreen mode Exit fullscreen mode

The interactive reference at /reference documents all 39 REST API route modules, including the full policy API. The OpenAPI 3.0 spec is available at /doc if you want to generate a client.

# 5. Verify your policies work with dry-run before going live
curl -X POST http://127.0.0.1:3100/v1/transactions/send \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer wai_sess_<token>" \
  -d '{"type": "APPROVE", "spender": "0xDEF1...", "amount": "999999999999", "dryRun": true}'
Enter fullscreen mode Exit fullscreen mode

Confirm you get POLICY_DENIED back. Then test a legitimate, in-policy approval to confirm that passes too. Only then deploy your agent to mainnet.

What Self-Hosting Gives You That Hosted Services Don't

WAIaaS is self-hosted. The daemon runs in your infrastructure — a Docker container on your server, your laptop, your VPS. Your private keys never leave your environment. There's no third-party custody, no shared infrastructure, no API key rotation to worry about.

The Docker deployment is straightforward:

git clone https://github.com/waiaas/WAIaaS.git
cd WAIaaS
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

For production, use the secrets overlay to inject your master password via Docker Secrets rather than environment variables:

mkdir -p secrets
echo "your-secure-password" > secrets/master_password.txt
chmod 600 secrets/master_password.txt
docker compose -f docker-compose.yml -f docker-compose.secrets.yml up -d
Enter fullscreen mode Exit fullscreen mode

This matters for security-minded deployments: your master password never appears in process lists, Docker inspect output, or log files.

What's Next

If your threat model includes anything beyond simple token transfers — DeFi interactions, perpetual futures, cross-chain bridging — WAIaaS has policy types specifically covering those surfaces too. The PERP_MAX_LEVERAGE, LENDING_LTV_LIMIT, and VENUE_WHITELIST policies let you apply the same default-deny, explicitly-permitted approach to the more complex operations your agent might perform.

The full source code, architecture documentation, and deployment guides are at github.com/waiaas/WAIaaS. The project home, including documentation and release notes, is at waiaas.ai.

Top comments (0)