APPROVE_TIER_OVERRIDE: Emergency Controls for High-Stakes DeFi Operations

APPROVE_TIER_OVERRIDE: Emergency Controls for High-Stakes DeFi Operations

Giving an AI agent a wallet without guardrails is like giving a toddler a credit card — the intentions might be fine, but the outcomes can be catastrophic. If you're building systems where AI agents execute DeFi operations autonomously, the question isn't whether something will go wrong, it's whether your security architecture will contain the damage when it does. WAIaaS is an open-source, self-hosted Wallet-as-a-Service built specifically for this problem, and this post walks through exactly how its layered security model — and specifically the APPROVE_TIER_OVERRIDE policy — keeps high-stakes DeFi operations under human control.

Why This Matters More Than You Think

Autonomous AI agents executing DeFi trades, managing lending positions, or running arbitrage strategies represent a genuinely new threat surface. The agent doesn't get tired, doesn't second-guess itself, and won't pause before submitting a transaction that wipes out a position. Traditional wallet security was designed for humans who read confirmations. It was not designed for a loop that fires 200 transactions per hour.

The stakes compound quickly. A misconfigured leverage position, a swap against a manipulated price feed, an approval transaction that grants unlimited spend to a malicious contract — any of these can drain a wallet in seconds. If your security model is "we trust the agent's logic," you're one bug away from a very bad day.

The honest answer to this problem is layered, explicit security that does not trust the agent by default. That's the design philosophy behind WAIaaS.

The 3-Layer Security Model

WAIaaS structures security as three concentric layers, each independent of the others:

Layer 1 — Session Authentication: Every agent gets a session token (wai_sess_...) with configurable TTL, maximum renewals, and an absolute lifetime. The agent can only operate within its session scope.

Layer 2 — Policy Engine: A 21-type policy engine with default-deny enforcement. Transactions are blocked unless explicitly allowed. This is where APPROVE_TIER_OVERRIDE lives.

Layer 3 — Monitoring and Kill Switch: Incoming transaction monitoring with real-time notifications and owner-controlled approval channels via WalletConnect, Telegram, or push relay.

Three separate authentication methods map to three roles: masterAuth (Argon2id) for the system administrator who creates wallets and configures policies, sessionAuth (JWT HS256) for the AI agent, and ownerAuth (SIWS/SIWE signatures) for the fund owner who approves transactions. The agent never holds the master password. The owner's approval is cryptographic, not just a button click.

Default-Deny: The Foundation

Before getting into APPROVE_TIER_OVERRIDE specifically, you need to understand the default-deny posture. In WAIaaS, if you create a wallet and give an agent a session token without configuring policies, the agent cannot transfer tokens that aren't on an explicit whitelist, and cannot call contracts that aren't on an explicit whitelist.

This is not a "warn on suspicious activity" model. It's a hard block. The error response is unambiguous:

{
  "error": {
    "code": "POLICY_DENIED",
    "message": "Transaction denied by SPENDING_LIMIT policy",
    "domain": "POLICY",
    "retryable": false
  }
}

To allow token transfers, you configure an ALLOWED_TOKENS policy:

curl -X POST http://localhost:3100/v1/policies \
  -H 'Content-Type: application/json' \
  -H 'X-Master-Password: <password>' \
  -d '{
    "walletId": "<wallet-uuid>",
    "type": "ALLOWED_TOKENS",
    "rules": {
      "tokens": [
        {
          "address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
          "symbol": "USDC",
          "chain": "solana"
        }
      ]
    }
  }'

Your agent can't touch tokens you haven't explicitly allowed. That's the foundation everything else builds on.

The 4 Security Tiers

The SPENDING_LIMIT policy assigns every transaction to one of four tiers based on its USD value:

Here's what a realistic SPENDING_LIMIT configuration looks like for a DeFi trading agent:

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": "SPENDING_LIMIT",
    "rules": {
      "instant_max_usd": 100,
      "notify_max_usd": 500,
      "delay_max_usd": 2000,
      "delay_seconds": 900,
      "daily_limit_usd": 5000
    }
  }'

With this configuration: swaps under $100 execute immediately, swaps between $100–$500 execute but you get notified, swaps between $500–$2,000 are queued for 15 minutes (giving you a window to cancel), and anything above $2,000 requires your explicit approval. The daily limit caps total exposure regardless of individual transaction size.

The tier assignment is deterministic: amount <= instant_max → INSTANT, <= notify_max → NOTIFY, <= delay_max → DELAY, > delay_max → APPROVAL.

APPROVE_TIER_OVERRIDE: Why It Exists

Here's the problem APPROVE_TIER_OVERRIDE solves. Your SPENDING_LIMIT policy handles regular transfers based on their USD value. But APPROVE transactions — token approvals — don't transfer funds. They grant a smart contract permission to transfer funds later, potentially without limit.

A $0 approval transaction could grant an unlimited spend allowance to a DEX router. Under a pure spending-limit model, that $0 transaction sails through as INSTANT because it moves no funds. But the downstream risk is enormous.

APPROVE_TIER_OVERRIDE forces token approval transactions into a specific security tier regardless of their dollar value. You can force every approval operation to require human sign-off, every time, no matter how small the nominal value appears to the spending limit check.

This is the right model for high-stakes DeFi operations. You want your agent to execute swaps autonomously within limits — that's the whole point — but you want to personally review every new approval that grants a contract access to your tokens.

The complementary policy APPROVE_AMOUNT_LIMIT lets you block unlimited approvals entirely, ensuring your agent only grants bounded allowances even when approvals are permitted.

Simulate Before You Execute

Before any transaction reaches the policy engine in production, you can run it through the simulation API. The dryRun flag submits the full transaction payload through the 7-stage pipeline — validation, auth, policy check, gas condition evaluation — without broadcasting to the network:

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": "TRANSFER",
    "to": "recipient-address",
    "amount": "0.1",
    "dryRun": true
  }'

This is particularly useful when you're configuring policies for a new DeFi strategy. Run representative transactions through dry-run first to verify they land in the tier you expect before your agent starts operating in production. A transaction you thought would be NOTIFY hitting APPROVAL in production because you miscalculated the USD value is an annoying surprise. Catching it in dry-run is just good engineering.

Human Approval Channels

When a transaction requires APPROVAL tier, the owner receives a notification and must cryptographically approve it. The approval uses ownerAuth — the owner signs a message with their private key (ed25519 for Solana, secp256k1 for EVM):

curl -X POST http://127.0.0.1:3100/v1/transactions/<tx-id>/approve \
  -H "X-Owner-Signature: <ed25519-or-secp256k1-signature>" \
  -H "X-Owner-Message: <signed-message>"

WAIaaS supports three signing channels for delivering these approval requests: push relay, Telegram, and WalletConnect. The WalletConnect integration means the owner can approve transactions from any WalletConnect-compatible wallet app — a familiar interface for anyone in the DeFi space.

For DeFi operations that involve high-value positions, perpetual futures, or new contract interactions, the approval channel gives you a hard checkpoint that can't be bypassed by agent logic.

Policies Purpose-Built for DeFi

Beyond the general-purpose policies, WAIaaS includes several policy types designed specifically for DeFi risk management:

These aren't bolt-ons. They're part of the same policy engine that handles spending limits and token whitelists, applied through the same default-deny framework, enforced in the same transaction pipeline.

Standing Up the Full Stack

The fastest path from zero to a secured DeFi agent runs through the CLI:

npm install -g @waiaas/cli
waiaas init                        # Create data directory + config.toml
waiaas start                       # Start daemon (sets master password on first run)
waiaas quickset --mode mainnet     # Create wallets + MCP sessions in one step

Or if you prefer Docker:

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

The Docker image runs as a non-root user (UID 1001), exposes only 127.0.0.1:3100 by default, and supports Docker Secrets for production secret management. For production deployments, use the secrets overlay:

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

Once the daemon is running, create a wallet, attach policies, generate a session token for your agent, and test your policy configuration with dry-run before going live. The OpenAPI 3.0 spec is available at http://127.0.0.1:3100/doc and the interactive Scalar reference UI at http://127.0.0.1:3100/reference — useful for exploring the full policy configuration options.

Using the TypeScript SDK in Your Agent

If you're building the agent itself rather than just the infrastructure, the TypeScript SDK gives you typed access to the full API:

import { WAIaaSClient, WAIaaSError } from '@waiaas/sdk';

const client = new WAIaaSClient({
  baseUrl: process.env['WAIAAS_BASE_URL'] ?? 'http://localhost:3100',
  sessionToken: process.env['WAIAAS_SESSION_TOKEN'],
});

try {
  const tx = await client.sendToken({
    type: 'TRANSFER',
    to: 'recipient-address',
    amount: '0.001',
  });
  console.log(`Transaction submitted: ${tx.id} (status: ${tx.status})`);
} catch (error) {
  if (error instanceof WAIaaSError) {
    console.error(`API Error: [${error.code}] ${error.message}`);
    // POLICY_DENIED surfaces here — handle gracefully, not with a retry loop
  }
}

The SDK is zero external dependencies. A POLICY_DENIED error with retryable: false means exactly that — the policy blocked it and retrying won't help. Your agent should surface that to a monitoring system or log it, not hammer the API hoping the policy changes.

The Security Architecture in One Mental Model

If you want a single frame for how this all fits together: WAIaaS assumes the agent will eventually do something wrong — a bug, a bad inference, a manipulated input — and builds the security architecture around containing that failure rather than preventing it through correctness.

The session token limits the blast radius to a single wallet with a configured scope. The policy engine's default-deny posture means misconfigured agent permissions fail closed rather than open. The spending limit tiers route high-value operations through human review. APPROVE_TIER_OVERRIDE ensures approval transactions never slip through on a technicality. The 15-minute delay window gives you time to intervene before a queued transaction executes.

None of these layers individually make your agent secure. Together, they mean a compromised or buggy agent has a very hard time causing catastrophic damage.

What's Next

The full policy reference, including all 21 policy types and their configuration schemas, is available in the interactive API docs at http://127.0.0.1:3100/reference once your daemon is running. For MCP-based agent integration with Claude Desktop, the waiaas mcp setup --all command handles the configuration automatically — your agent gets access to 45 MCP tools including DeFi positions, transaction simulation, and approval workflows without writing integration code.

The codebase is open source and self-hosted — you run it, you control the keys, and the policy engine is yours to inspect and extend.