Simulate Before You Trade: Dry-Run APIs for High-Stakes DeFi Bots

Simulate Before You Trade: Dry-Run APIs for High-Stakes DeFi Bots

Automated trading bots live and die on execution quality — but sending a live transaction to test whether your DeFi strategy will work is an expensive way to find out it won't. Whether you're running an arb loop across Jupiter and Drift, a cross-chain bridge strategy using LI.FI and Across, or a liquidation bot on Aave v3, you need to know what's going to happen before block confirmation, not after. WAIaaS ships a dry-run API that lets your bot simulate any transaction through the full execution pipeline — policy checks, gas estimation, balance validation — without touching the chain.

Why Simulation Matters at the Edge of the Mempool

In DeFi trading, the failure modes that hurt most aren't the ones you see coming. Your bot catches a Jupiter arbitrage window. It builds the transaction. It fires. Then: insufficient balance for gas, or a spending limit policy blocks the swap because the USD value crossed a threshold, or the contract isn't on the whitelist you forgot to update last week.

Each of those outcomes costs you the opportunity. Some cost you gas too. At scale — if your bot is running hundreds of transactions per day across multiple protocols — the cumulative loss from failed transactions and missed opportunities compounds fast.

Simulation gives you a deterministic pre-flight check. You confirm that the transaction passes validation, policy enforcement, and balance checks before committing. Your bot can branch on the result: execute if clean, skip or alert if not. That's the difference between a bot that survives for months and one that drains its own account on failed gas.

The WAIaaS Execution Pipeline

Before getting into the dry-run API itself, it helps to understand what a transaction actually goes through in WAIaaS. There's a 7-stage pipeline: validate, auth, policy, wait, execute, confirm, and completion stages. The dry-run runs your transaction through stages 1–3 (validation, auth, policy) and returns exactly what would happen — without ever reaching stage 5 (execute).

That means you get:

What you don't get is an on-chain simulation of the swap output. For that, you'd query the protocol directly (Jupiter's quote API, for example). The WAIaaS dry-run is about infrastructure reliability — will this transaction clear the wallet layer before it even hits the RPC.

Using the Dry-Run API

The dry-run is a single flag on the standard send endpoint. Add "dryRun": true to any transaction payload and WAIaaS returns the full policy evaluation result without executing.

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
  }'

Your bot gets back either a clean pass — meaning the transaction would proceed through policy and into execution — or a structured error response:

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

The retryable field is particularly useful for bot logic. A false here means no point retrying — this transaction is structurally blocked. A true would indicate a transient condition (like rate limiting) where retry makes sense.

Wiring Dry-Run into a Trading Bot

Here's a practical pattern for a bot that needs to execute a token transfer as part of a larger trade flow:

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'],
});

async function executeSafeTransfer(to: string, amount: string) {
  // Step 1: Dry-run to validate before committing
  try {
    await client.sendToken({
      type: 'TRANSFER',
      to,
      amount,
      dryRun: true,
    } as any); // dryRun flag passed through to the API
    console.log('Dry-run passed — proceeding to execution');
  } catch (error) {
    if (error instanceof WAIaaSError) {
      console.error(`Dry-run blocked: [${error.code}] ${error.message}`);
      // Bot branches here: skip opportunity, alert operator, adjust strategy
      return null;
    }
    throw error;
  }

  // Step 2: Execute the real transaction
  const tx = await client.sendToken({ type: 'TRANSFER', to, amount });
  console.log(`Submitted: ${tx.id}`);

  // Step 3: Poll for confirmation
  const timeout = 60_000;
  const start = Date.now();
  while (Date.now() - start < timeout) {
    const result = await client.getTransaction(tx.id);
    if (result.status === 'COMPLETED') {
      console.log(`Confirmed: ${result.txHash}`);
      return result;
    }
    if (result.status === 'FAILED') {
      console.error(`Failed: ${result.error}`);
      return null;
    }
    await new Promise(r => setTimeout(r, 1000));
  }
  return null;
}

This pattern — simulate, branch, execute, poll — is the foundation for any reliable trading bot. The dry-run adds a single round-trip latency cost but eliminates an entire class of failed-execution failures.

Policy Engine: Your Bot's Risk Layer

For trading bots specifically, the WAIaaS policy engine is worth configuring carefully. WAIaaS has 21 policy types with 4 security tiers: INSTANT, NOTIFY, DELAY, and APPROVAL.

The tier assignment for spending limits works like this: amount ≤ instant_max → INSTANT (execute immediately), ≤ notify_max → NOTIFY (execute, send alert), ≤ delay_max → DELAY (queue for N seconds, cancellable), > delay_max → APPROVAL (require human sign-off).

For a trading bot, you probably want a policy like this:

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
    }
  }'

This means your bot can fire transactions under $100 with zero friction. Between $100–$500 it executes but you get notified. Between $500–$2000 it queues for 15 minutes (giving you a window to cancel if something looks wrong). Above $2000 it requires manual approval.

Critically, WAIaaS uses default-deny for token and contract access. If you haven't explicitly whitelisted the tokens your bot trades or the contracts it calls, transactions are blocked. For a trading bot, set up ALLOWED_TOKENS and CONTRACT_WHITELIST explicitly for each protocol:

# Whitelist USDC on Solana
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": "ALLOWED_TOKENS",
    "rules": {
      "tokens": [{"address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "symbol": "USDC", "chain": "solana"}]
    }
  }'

When your dry-run hits a POLICY_DENIED with code CONTRACT_WHITELIST, you know exactly what to add.

DeFi Protocol Coverage for Trading Bots

WAIaaS integrates 15 DeFi protocol providers. For trading-focused bots, the relevant ones include:

For a cross-protocol strategy — say, swap SOL for USDC on Jupiter, then bridge to Ethereum via LI.FI, then deploy into Aave — you're doing this all through one WAIaaS daemon, one session token, one wallet abstraction. The DeFi action endpoint follows a consistent pattern:

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"
  }'

WAIaaS supports 2 chain types (Solana and EVM) across 18 networks, so your bot isn't locked to a single chain.

Gas Conditional Execution

One feature that's directly relevant to trading bots: WAIaaS has a gas conditional execution stage in the pipeline. Transactions can be configured to execute only when gas price meets a threshold — so your bot queues a transaction and it only fires when conditions are favorable, rather than burning high-gas windows on non-urgent operations.

For MEV-adjacent strategies where gas timing matters, this integrates cleanly with the dry-run pattern. Simulate first to confirm the transaction is policy-clean, then submit with gas conditions to let the pipeline handle execution timing.

The Three Auth Layers Your Bot Uses

WAIaaS has three authentication layers, and as a bot builder, you interact with all three:

For bot operation, you generate a session token with masterAuth and hand it to your bot's environment. The bot never touches the master password.

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>"}'

Sessions support per-session TTL, maxRenewals, and absoluteLifetime configuration, so you can scope your bot's session to exactly how long it should run without manual intervention.

Quick Start for Bot Builders

1. Deploy WAIaaS locally

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

2. Create a wallet and configure policies

# Create 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"}'

# Set spending limits
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}}'

3. Generate 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>"}'

4. Install the SDK and implement the dry-run → execute pattern

npm install @waiaas/sdk

Use the TypeScript example from the Simulation section above to wire your bot's transaction logic.

5. Explore the full API

open http://127.0.0.1:3100/reference

The interactive Scalar API reference at /reference lists all 39 route modules. The OpenAPI 3.0 spec is at /doc.

What's Next

The dry-run API is one piece of a larger infrastructure stack for production trading bots. WAIaaS also ships ERC-4337 Account Abstraction for gasless transaction patterns, WalletConnect integration for human-in-the-loop approval of large trades, and incoming transaction monitoring for tracking deposits in real time.

If you're building at scale, the full codebase — including the 15-package monorepo, 683+ test files, and CLI with 20 commands — is open source and self-hosted, meaning your wallet infrastructure runs on your infrastructure, not a third party's.

The code is at https://github.com/minhoyoo-iotrust/WAIaaS and the project site is at https://waiaas.ai. Start with docker compose up -d and the quickstart CLI command — you'll have a working wallet daemon with policy enforcement running in under five minutes.