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:
- Schema validation (is this a valid transaction structure?)
- Auth verification (does the session token have access to this wallet?)
- Full policy enforcement (spending limits, token whitelists, contract whitelists, rate limits)
- Balance pre-check
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:
- jupiter-swap — Solana DEX aggregator
- drift — Perpetual futures and spot on Solana
- hyperliquid — Perpetual futures, spot trading, and sub-accounts
- zerox-swap — EVM DEX aggregation
- aave-v3 — EVM lending (useful for collateral management in leveraged strategies)
- lifi — Cross-chain bridging
- across — Cross-chain bridging
- pendle — Yield trading
- polymarket — Prediction market trading
- dcent-swap — Swap integration
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:
- masterAuth (Argon2id) — You use this to create wallets, configure policies, and generate session tokens. Your bot's infrastructure setup, not its hot path.
- sessionAuth (JWT HS256) — This is what your bot uses for every transaction. Short-lived, scoped to a wallet, passed as a Bearer token.
- ownerAuth (SIWE/SIWS) — Used for approving transactions that hit the APPROVAL tier, and for the kill switch if something goes wrong.
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.