Nonce Collision Prevention: How WAIaaS Manages Transaction Order for AI Agents Across 18 Networks
Nonce collision is one of the most frustrating problems you'll hit when building AI agents that send blockchain transactions — and it gets worse the moment your agent tries to operate across multiple networks simultaneously. If your agent fires two transactions at once (which AI agents absolutely will do), and both grab the same nonce, one of them silently fails. No error thrown. No retry. Just a lost transaction and a confused agent.
This post walks through why nonce management is uniquely hard for AI agents, how WAIaaS handles it through its 7-stage transaction pipeline, and how you can get an agent submitting safe, ordered transactions across 18 networks in about 10 minutes.
Why Nonce Collisions Are an AI Agent Problem, Not Just a Blockchain Problem
Human wallets rarely hit nonce collisions. A person clicks "send," waits for confirmation, then sends again. Sequential by nature.
AI agents don't work that way. LangChain agents, CrewAI crews, AutoGPT loops, Claude via MCP — they're all capable of initiating multiple actions in parallel or in rapid succession. An agent managing a DeFi portfolio might simultaneously:
- Swap SOL for USDC on Jupiter
- Stake ETH via Lido
- Submit a limit order on Hyperliquid
- Pay for an API call via x402
If those four actions hit the same wallet, on even two different chains, you need something that tracks nonce state correctly, queues competing transactions, and retries failed submissions — all without the agent needing to know any of this is happening.
That's the infrastructure problem WAIaaS solves. The agent just calls an API. WAIaaS handles the rest.
What Makes Multi-Network Nonce Management Hard
Before getting to the solution, it helps to understand what you're actually dealing with.
Nonces are per-account, per-chain. Your Ethereum wallet has its own nonce sequence. Your Solana wallet uses a different mechanism (recent blockhash slots). A wallet on Arbitrum has its own nonce counter separate from Ethereum mainnet, even if it's the same private key. WAIaaS supports 18 networks across 2 chain types (EVM and Solana), which means nonce state has to be tracked independently for each.
Concurrent submissions create races. If two processes read nonce 42 from the chain at the same moment and both submit with nonce 42, the network accepts one and silently drops the other. No exception. No revert. The second transaction just never exists.
AI agents are naturally concurrent. Even a single Claude conversation can trigger tool calls that fan out into multiple API requests. The MCP server for WAIaaS exposes 45 tools. An agent using send-batch, hyperliquid, and send-token in the same response is not unusual.
Stuck transactions block everything after them. If nonce 42 gets stuck (gas too low, RPC timeout, whatever), nonces 43, 44, 45 are all waiting behind it. The agent appears frozen.
How WAIaaS Handles This: The 7-Stage Pipeline
Every transaction submitted to WAIaaS — whether via REST API, TypeScript SDK, Python SDK, or any of the 45 MCP tools — goes through a 7-stage pipeline:
stage1-validate → stage2-auth → stage3-policy → stage4-wait → stage5-execute → stage6-confirm → stages
The key stage for nonce safety is stage4-wait. This is where WAIaaS serializes transactions before they touch the network.
Here's what that means in practice:
- Transactions for the same wallet+chain are queued and executed in order
- Nonce assignment happens immediately before broadcast, not at submission time
- If execution fails, the pipeline can retry without creating a gap in the nonce sequence
- The agent's API call returns a transaction ID immediately; confirmation is async
The agent doesn't wait for blockchain confirmation before it can do something else. It submits, gets an ID, and can poll or move on. WAIaaS handles the actual sequencing.
The Agent's View: Simple API, Complex Safety
From the agent's perspective, sending a transaction looks like this:
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"
}'
The agent doesn't specify a nonce. It doesn't know what network the wallet is on (that's configured at wallet creation time). It just says "send 0.1" and gets back a transaction ID.
Behind that call, WAIaaS is:
- Validating the request format
- Checking the session token is valid and not expired
- Running the transaction through the policy engine (21 policy types, default-deny)
- Queuing it in the pipeline behind any in-flight transactions for that wallet
- Assigning the correct nonce at execution time
- Submitting to the RPC endpoint
- Polling for confirmation
If you want to test a transaction without actually submitting it, use the dry-run flag:
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 runs the transaction through validation, auth, and policy stages but stops before execution. You get back what would have happened, including policy verdicts, without touching the chain. Useful for testing agent logic before letting it loose with real funds.
Multiple Wallets, Multiple Networks: The MCP Approach
If your agent needs to operate on multiple chains simultaneously — say, ETH on mainnet and SOL on Solana mainnet — the cleanest pattern is one MCP server per wallet. Each wallet has its own session token, its own policy set, and its own nonce sequence. There's no shared state that can collide.
The Claude Desktop config for this looks like:
{
"mcpServers": {
"waiaas-trading": {
"command": "npx",
"args": ["-y", "@waiaas/mcp"],
"env": {
"WAIAAS_BASE_URL": "http://127.0.0.1:3100",
"WAIAAS_AGENT_ID": "019c47d6-51ef-7f43-a76b-d50e875d95f4",
"WAIAAS_AGENT_NAME": "trading-agent",
"WAIAAS_DATA_DIR": "~/.waiaas"
}
},
"waiaas-solana": {
"command": "npx",
"args": ["-y", "@waiaas/mcp"],
"env": {
"WAIAAS_BASE_URL": "http://127.0.0.1:3100",
"WAIAAS_AGENT_ID": "019c4cd2-86e8-758f-a61e-9c560307c788",
"WAIAAS_AGENT_NAME": "solana-wallet",
"WAIAAS_DATA_DIR": "~/.waiaas"
}
}
}
}
Each MCP server instance connects to a different wallet on the same WAIaaS daemon. Claude sees two separate sets of tools — waiaas-trading tools operate on the EVM wallet, waiaas-solana tools operate on the Solana wallet. Transactions submitted through either path are independently queued and sequenced. They cannot interfere with each other's nonce state.
The CLI can auto-generate this entire configuration:
waiaas mcp setup --all
This registers all wallets known to the daemon and prints the JSON you paste into Claude Desktop config.
Policy as a Safety Net for Agent Mistakes
Nonce ordering keeps transactions from colliding at the protocol level. But AI agents can also make logical mistakes — sending to the wrong address, submitting unexpectedly large amounts, or looping on an action that should have stopped.
WAIaaS's policy engine adds a second layer. With 21 policy types and 4 security tiers (INSTANT, NOTIFY, DELAY, APPROVAL), you can set hard limits on what the agent can actually do — regardless of what it asks for.
A sensible starting policy for an agent you're testing:
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:
- Under $100: executes immediately (INSTANT)
- $100–$500: executes immediately but you get notified (NOTIFY)
- $500–$2000: waits 15 minutes before executing, giving you a cancellation window (DELAY)
- Over $2000: requires your explicit approval via WalletConnect or Telegram (APPROVAL)
Combined with ALLOWED_TOKENS and CONTRACT_WHITELIST (both default-deny — the agent literally cannot interact with tokens or contracts you haven't whitelisted), you have a system where agent bugs hit policy walls before they hit the blockchain.
When a transaction is blocked by policy, the response looks like this:
{
"error": {
"code": "POLICY_DENIED",
"message": "Transaction denied by SPENDING_LIMIT policy",
"domain": "POLICY",
"retryable": false
}
}
The agent sees a clear error code it can handle, log, or report back to the user.
Building an Agent with the TypeScript SDK
For agents built in code rather than via MCP, the TypeScript SDK gives you the same pipeline access with a clean async interface:
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'],
});
// Check balance before acting
const balance = await client.getBalance();
console.log(`Balance: ${balance.balance} ${balance.symbol} (${balance.chain}/${balance.network})`);
// Submit transaction — nonce handled by WAIaaS
const sendResult = await client.sendToken({
type: 'TRANSFER',
to: 'recipient-address',
amount: '0.001',
});
console.log(`Transaction submitted: ${sendResult.id} (status: ${sendResult.status})`);
// Poll for confirmation
const POLL_TIMEOUT_MS = 60_000;
const startTime = Date.now();
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
const tx = await client.getTransaction(sendResult.id);
if (tx.status === 'COMPLETED') {
console.log(`Transaction confirmed! Hash: ${tx.txHash}`);
break;
}
if (tx.status === 'FAILED') {
console.error(`Transaction failed: ${tx.error}`);
break;
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
Notice the pattern: submit, get an ID, poll. The agent isn't blocking on blockchain confirmation. It can do other work between polls. And if two parts of the agent submit transactions simultaneously, WAIaaS serializes them correctly — the agent just sees two IDs to track.
Error handling is straightforward:
try {
const tx = await client.sendToken({ to: '...', amount: '1.0' });
} catch (error) {
if (error instanceof WAIaaSError) {
console.error(`API Error: [${error.code}] ${error.message}`);
// error.code examples: INSUFFICIENT_BALANCE, POLICY_DENIED, TOKEN_EXPIRED
}
}
Quick Start: Agent with Safe Transaction Ordering in 5 Steps
Step 1 — Install and start WAIaaS
npm install -g @waiaas/cli
waiaas init
waiaas start
Step 2 — Create wallets and sessions
waiaas quickset --mode mainnet
This creates wallets and MCP sessions in one command.
Step 3 — Set up policies before giving the agent access
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
}
}'
Step 4 — Connect Claude via MCP
waiaas mcp setup --all
Paste the output into your Claude Desktop config.
Step 5 — Test with a dry run
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
}'
Confirm the pipeline responds correctly before sending real funds.
What's Next
The nonce problem is infrastructure. Once it's solved, the interesting work is building agents that actually do something useful — swapping tokens, managing DeFi positions, paying for their own API calls with x402. WAIaaS has 15 DeFi protocol integrations and 45 MCP tools ready to use once your transaction pipeline is stable.
Browse the full API reference at http://127.0.0.1:3100/reference after starting the daemon — it's an interactive Scalar UI generated from the OpenAPI 3.0 spec, covering all 39 REST API route modules. Everything your agent can call is documented there with live examples.
Get started:
- GitHub: https://github.com/minhoyoo-iotrust/WAIaaS
- Official site: https://waiaas.ai
Top comments (0)