DEV Community

Cover image for RATE_LIMIT Policy: Prevent Your DeFi Bot from Getting Rekt by MEV
Wallet Guy
Wallet Guy

Posted on

RATE_LIMIT Policy: Prevent Your DeFi Bot from Getting Rekt by MEV

RATE_LIMIT Policy: Prevent Your DeFi Bot from Getting Rekt by MEV

Your DeFi bot is firing transactions faster than you can monitor them — and without rate limiting baked into your wallet infrastructure, you're not just vulnerable to MEV, you're handing over free money to every searcher watching the mempool. Whether you're running an arb loop, a liquidation bot, or a multi-protocol yield strategy, uncontrolled transaction velocity is one of the fastest ways to blow up a wallet.

Why Transaction Rate Control Is a First-Class Problem

Here's a scenario most bot builders have lived through: your arb strategy detects a pricing discrepancy between two DEX pools. Your bot fires. Then fires again. Then fires a third time because the first confirmation hasn't landed yet and your loop hasn't exited. You've just broadcast three competing transactions from the same wallet, front-run yourself, and paid gas on all three — two of which reverted.

MEV bots didn't steal that trade. You handed it to them.

Rate limiting at the infrastructure level — not just in your application code — is the difference between a bot that survives market volatility and one that drains itself dry during a busy block. Application-level rate limiting is fragile: a restart, a race condition, a missed exception handler, and your throttle is gone. You need the wallet itself to enforce limits, independent of whatever application logic is running above it.

This is where WAIaaS's RATE_LIMIT policy comes in.

What RATE_LIMIT Actually Does

WAIaaS has a policy engine with 21 policy types and 4 security tiers: INSTANT, NOTIFY, DELAY, and APPROVAL. The RATE_LIMIT policy caps how many transactions your wallet can submit within a given period — and it enforces this at the daemon level, before execution, inside the 7-stage transaction pipeline.

That means no matter what your bot code does — whether it crashes and restarts in a loop, has a logic bug that triggers duplicate sends, or gets compromised and starts spamming — the wallet will hard-stop once the rate limit is hit.

The policy configuration is simple:

{"maxTransactions": 10, "period": "hourly"}
Enter fullscreen mode Exit fullscreen mode

You set the max number of transactions allowed per period, and any submission beyond that gets rejected with a structured error before it ever touches the chain.

Setting Up RATE_LIMIT for Your Trading Bot

First, you need a wallet. Here's the full flow from zero to rate-limited bot wallet using the REST API:

Create the wallet:

curl -X POST http://127.0.0.1:3100/v1/wallets \
  -H "Content-Type: application/json" \
  -H "X-Master-Password: my-secret-password" \
  -d '{"name": "trading-wallet", "chain": "solana", "environment": "mainnet"}'
Enter fullscreen mode Exit fullscreen mode

Create a session for your bot:

curl -X POST http://127.0.0.1:3100/v1/sessions \
  -H "Content-Type: application/json" \
  -H "X-Master-Password: my-secret-password" \
  -d '{"walletId": "<wallet-uuid>"}'
Enter fullscreen mode Exit fullscreen mode

Apply the RATE_LIMIT policy:

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": "RATE_LIMIT",
    "rules": {
      "maxTransactions": 10,
      "period": "hourly"
    }
  }'
Enter fullscreen mode Exit fullscreen mode

Ten transactions per hour is a conservative starting point. For a high-frequency arb bot you might push that higher. For a yield rebalancer that only needs to act a few times a day, you'd set it lower. The point is that it's explicit, enforced at the wallet level, and observable.

When your bot hits the limit, it gets a structured error response — not a chain revert, not a silent failure:

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

Your bot can catch this, log it, and back off — rather than spiraling into a retry loop that chews through gas.

Layering RATE_LIMIT With SPENDING_LIMIT for Compound Protection

RATE_LIMIT controls frequency. SPENDING_LIMIT controls size. Used together, they give you two independent axes of risk control — and WAIaaS's policy engine applies both simultaneously.

Here's a SPENDING_LIMIT policy configured for a trading bot 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": "SPENDING_LIMIT",
    "rules": {
      "instant_max_usd": 100,
      "notify_max_usd": 500,
      "delay_max_usd": 2000,
      "delay_seconds": 900,
      "daily_limit_usd": 5000
    }
  }'
Enter fullscreen mode Exit fullscreen mode

With this configuration, the 4-tier logic works like this:

  • Under $100 → INSTANT: executes immediately, no notification
  • $100–$500 → NOTIFY: executes immediately, but you get an alert
  • $500–$2000 → DELAY: queued for 15 minutes, cancellable
  • Over $2000 → APPROVAL: requires human sign-off via WalletConnect or push notification

For a trading bot, this means your routine small swaps execute at full speed, while anything anomalously large trips the delay or approval gate. If a bug in your bot logic generates a massive unexpected trade, it doesn't execute immediately — you have a window to intervene.

Gas Conditional Execution: Don't Trade When It's Expensive

Beyond rate limiting, WAIaaS has gas conditional execution built into its transaction pipeline. Transactions execute only when gas price meets your configured threshold.

For Solana bots on Jupiter this is less of a concern, but for EVM bots — especially anything interacting with Aave, Pendle, or Hyperliquid on Ethereum-based chains — gas timing can make or break a strategy. An arb that's profitable at 20 gwei becomes a loss at 80 gwei.

Gas-conditional execution gives your bot a structural filter: it only fires when execution cost is within your acceptable range.

Multi-Protocol Access: One Wallet, 15 DeFi Integrations

The other reason to run your trading infrastructure through WAIaaS is the breadth of DeFi protocol access. WAIaaS integrates 15 DeFi protocol providers:

  • Jupiter (Solana DEX aggregation)
  • Drift (Solana perpetuals)
  • Hyperliquid (perpetual futures, spot trading, sub-accounts)
  • Aave V3 (lending/borrowing)
  • Kamino (Solana lending)
  • LI.FI + Across (cross-chain bridging)
  • Lido + Jito (liquid staking)
  • 0x + D'CENT swap (EVM DEX aggregation)
  • Pendle (yield trading)
  • Polymarket (prediction markets)
  • XRP Ledger DEX (XRPL native DEX)
  • ERC-8004 (onchain agent reputation)

A multi-leg strategy — say, borrow on Aave, swap on 0x, bridge via LI.FI, then stake the output on Lido — runs through a single wallet, a single session token, and a single policy set. You're not managing credentials and rate limits across five different integrations.

Here's what a Jupiter swap looks like through the API:

curl -X POST http://127.0.0.1:3100/v1/actions/jupiter-swap/swap \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer wai_sess_<token>" \
  -d '{
    "inputMint": "So11111111111111111111111111111111111111112",
    "outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "amount": "1000000000"
  }'
Enter fullscreen mode Exit fullscreen mode

That's a SOL → USDC swap. The wallet handles signing, the policy engine checks it against your RATE_LIMIT and SPENDING_LIMIT rules, and the transaction goes out — or gets blocked, depending on your configuration.

Dry-Run Before You Execute

Before your bot goes live, or before you commit to a trade in a new market condition, simulate it:

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
  }'
Enter fullscreen mode Exit fullscreen mode

The dryRun flag runs the transaction through the full 7-stage pipeline — including policy evaluation — without hitting the chain. You can verify that your bot's intended transaction would pass your rate limit, spending limit, token whitelist, and contract whitelist before committing real funds.

This is especially useful during strategy development: wire up your bot logic, run it in dry-run mode under load, and see exactly which transactions would have been blocked and why.

Quick Start: Bot Wallet With Rate Limiting in 5 Steps

  1. Install the CLI and start the daemon
npm install -g @waiaas/cli
waiaas init
waiaas start
Enter fullscreen mode Exit fullscreen mode
  1. Create a dedicated trading wallet
curl -X POST http://127.0.0.1:3100/v1/wallets \
  -H "Content-Type: application/json" \
  -H "X-Master-Password: my-secret-password" \
  -d '{"name": "trading-wallet", "chain": "solana", "environment": "mainnet"}'
Enter fullscreen mode Exit fullscreen mode
  1. Create a session token for your bot
curl -X POST http://127.0.0.1:3100/v1/sessions \
  -H "Content-Type: application/json" \
  -H "X-Master-Password: my-secret-password" \
  -d '{"walletId": "<wallet-uuid>"}'
Enter fullscreen mode Exit fullscreen mode
  1. Apply RATE_LIMIT and SPENDING_LIMIT policies
# Rate limit: 10 transactions per hour
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": "RATE_LIMIT",
    "rules": {"maxTransactions": 10, "period": "hourly"}
  }'

# Spending limit: tiered security by transaction size
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
    }
  }'
Enter fullscreen mode Exit fullscreen mode
  1. Run your first dry-run trade to verify policies are working
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}'
Enter fullscreen mode Exit fullscreen mode

The Infrastructure Layer Your Bot Actually Needs

The pattern here isn't complicated, but it's easy to skip when you're moving fast: enforce your risk rules at the infrastructure level, not in application code. Application code restarts, has bugs, and gets updated. A wallet-level policy enforced inside a 7-stage transaction pipeline before execution is a harder guarantee.

WAIaaS gives you 21 policy types to work with — rate limits, spending tiers, token whitelists, contract whitelists, network restrictions, venue whitelists, and more. For a trading bot, you'd typically combine RATE_LIMIT + SPENDING_LIMIT + ALLOWED_TOKENS + CONTRACT_WHITELIST to get a wallet that can only do exactly what your strategy requires, at the frequency and size your risk parameters allow.

The policy engine enforces default-deny: if ALLOWED_TOKENS or CONTRACT_WHITELIST isn't configured, those transaction types are blocked. Your bot can't accidentally interact with a contract you didn't explicitly whitelist.

What's Next

The full policy engine documentation, including all 21 policy types and their configuration schemas, is available via the interactive API reference at http://127.0.0.1:3100/reference once your daemon is running. For connecting your bot to Claude or other LLM-based agent frameworks, the 45 MCP tools in @waiaas/mcp cover the full trading surface — from balance queries to DeFi position management.

Check out the codebase and open issues on GitHub, and find the full documentation on the official site:

Top comments (0)