The Concurrency Problem — When 1,000 AI Agents Hit the Same Chain
One agent is easy. A single loop, a single wallet, a single RPC endpoint, a sendTransaction every few minutes. You can get away with almost anything.
Then someone says "scale it up." Now you have 1,000 agents, all signing transactions, all sharing infrastructure, and the chain starts fighting back. Not with dramatic errors — with silent ones. Transactions that never confirm, nonce gaps that brick your wallet, rate limits that eat your afternoon.
Here's what actually breaks when you run agents at volume, and how to engineer around it.
The First Wall: Nonce Management
Every transaction from a given wallet carries a sequence number — the nonce. The chain requires them to be used in order, exactly once, with no gaps. Gaps are catastrophic: a nonce gap freezes every transaction after it until the missing one is mined.
The naive multi-agent setup looks like this:
async def run_agent(agent_id: int, wallet: Wallet):
while True:
tx = await build_transaction(agent_id)
nonce = await get_next_nonce(wallet.address) # race waiting to happen
signed = wallet.sign(tx, nonce=nonce)
await send(signed)
await asyncio.sleep(random_interval())
get_next_nonce — that's the bug. Two agents call it at the same time, both get 42, both submit a transaction with nonce 42. One confirms, the other gets dropped. Now your wallet has a gap at 43 and every subsequent agent transaction stalls indefinitely.
The fix has three parts:
- Single nonce manager per wallet. One process owns the nonce counter and hands out increments under a lock. No agent touches the chain's nonce endpoint directly.
-
Pending-tx tracking. Don't trust the node's
pendingview — maintain your own map ofnonce → tx_hashand reconcile against confirmations. - Per-agent wallets. The cleanest fix of all: one keypair per agent. No shared nonce space, no cross-agent poisoning, and clean accounting when you need to audit which agent spent what.
The Second Wall: RPC Rate Limits
Public RPC endpoints rate-limit aggressively — often 20–40 requests per second per IP. An agent fleet doing getBalance, getFee, simulateTransaction, and sendTransaction for every action blows through that in seconds.
The naive response is to just retry on failure. The real response is to stop wasting calls:
- Cache state, don't poll. Track balances and nonces locally, invalidate only on your own sends or block events. Most agent loops re-read data they already know.
- Batch where the chain allows. JSON-RPC batch requests collapse 10 reads into 1 HTTP round trip.
- Rotate endpoints. Spread the fleet across multiple providers and local nodes. If one endpoint 429s, the fleet doesn't grind to a halt.
A useful pattern is a small circuit breaker around every RPC call:
class RpcPool:
def __init__(self, endpoints):
self.endpoints = [Endpoint(url) for url in endpoints]
async def call(self, method, params):
for ep in shuffled(self.endpoints):
if ep.is_cooling_down():
continue
try:
return await ep.call(method, params)
except RateLimited:
ep.cooldown(backoff())
raise FleetStalled()
Fail fast, fail over, never hammer a dead endpoint.
The Third Wall: Reorgs and Confirmation Confusion
Agents act on confirmations. But a block that looked confirmed at depth 1 can vanish in a reorg — and with it, the agent's entire worldview.
If your agent's next action depends on a previous transaction actually landing, you need:
- A minimum confirmation depth tuned to the chain's finality model (Solana finalizes in seconds; slower chains need more patience).
- Reconciliation jobs that re-check "did my transaction at nonce N actually make it?" after a reorg and re-submit if not.
-
Idempotent actions. Every agent action should carry a dedupe key — a hash of
(agent_id, action_type, target, params)— so re-submitting the same action after a reorg doesn't double-execute it.
This is where most "autonomous agent" demos quietly die: they work in a demo because the demo never reorgs. At scale, on real chains, they eventually do.
The Fourth Wall: Fees and the Gas Auction
When your fleet is small, you pay whatever the fee market demands. When your fleet is large, you are the fee market.
The trick is to make fee strategy per-transaction, not global:
- Priority classes. Settlement-critical actions (claiming rewards, closing positions) get aggressive fees. Housekeeping (updating state, logging) gets the cheap lane.
- Backoff, don't spam. If the mempool is congested, raise fees geometrically with a cap — then wait. Spamming the same transaction at the same fee just burns RPC budget.
- Schedule around congestion. On chains with predictable usage patterns, batch non-urgent transactions into low-activity windows.
What This Looks Like as an Architecture
The pattern that survives at scale:
┌─────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Agent Fleet │────▶│ Execution Core │────▶│ Chain Layer │
│ (per-agent │ │ nonce manager │ │ (multi-RPC, │
│ wallets) │ │ fee scheduler │ │ batching) │
└─────────────┘ │ idempotency keys│ └──────────────┘
└──────────────────┘
Agents stay dumb and single-purpose. The execution core owns nonces, fees, retries, and confirmation tracking. The chain layer abstracts the RPC mess. Each layer is independently scalable.
Notice what didn't make the diagram: cron scripts, shell loops, "just call the node directly." Those are how you start, not how you scale.
The Honest Takeaway
None of these problems are new — distributed systems has been solving nonce-like ordering, retry, and idempotency for decades. What's new is that blockchain adds external state you can't control: reorgs, fee markets, finality latency, rate limits. Your agents don't just talk to your own services; they negotiate with a live network that has opinions.
That's also why the teams I've seen succeed treat this as an infrastructure problem from day one, not a scripting problem. If you're building a fleet on a single chain, the four walls above are your roadmap. If you're building across many chains, each one brings its own flavor of the same four walls — different finality, different fee models, different RPC quirks.
If you'd rather not rebuild the execution core from scratch, platforms like BBIO exist precisely to front-load these lessons — a managed runtime for blockchain AI agents that handles nonce sequencing, multi-RPC failover, confirmation tracking, and per-agent key management across chains, so your team can focus on the agent logic that actually differentiates your product.
Either way: design for concurrency before you need it. The wall appears exactly when you can least afford to stop.
Top comments (0)