Over the past year, Base has gone from “promising L2” to a throughput workhorse. In July 2025, Dune’s analysis estimated that Base was contributing over 80% of L2 transaction fees, a proxy for real usage and demand.
Around the same time, Base rolled out Flashblocks, reducing block times from roughly two seconds to about 200 milliseconds, which significantly improves the speed at which on-chain systems can react. Pair that with GoldRush’s sub-second streaming feed (GraphQL over WebSockets). And you finally have the ingredients for practical, latency-aware AI agents that make decisions on fresh data—on Base, in production.
This post is a hands-on guide to building one of those agents end-to-end on Base. We’ll tap GoldRush for real-time market signals (GraphQL over WebSockets), convert those updates into a transparent prediction rule, and wire guarded contract calls that respect Base’s fee model. The aim is practical: a minimal agent you can read, run, and extend—grounded in the actual network settings and Flashblocks endpoints with the data plane you’ll use in production.
Base is an OP Stack Layer-2—EVM-compatible, inexpensive, and fast enough that “reactive” logic actually pays off. Great for bots and agents that need to try ideas without burning a hole in your wallet, Flashblocks' integration has improved performance up to 10x with Flashbots that stream sub-blocks at very low latency—200 ms.
Just as important: there’s already meaningful agent activity on Base. Coinbase’s developer team reports that over 20,000 agents have been deployed via AgentKit, and more than 600,000 on-chain transactions have been executed on Base and Base Sepolia as of April 14, 2025, a clear signal that agents are already active on this stack.
To cap it off, Covalent opened the GoldRush Streaming API to public beta in July 2025 with sub-second updates, then followed in August with “Speed Runs on Base.” Together, these make the Base ecosystem especially attractive for agents.
GoldRush (Covalent) gives you two data planes:
Streaming API (GraphQL over WebSockets): sub-second, structured events like OHLCV, new DEX pairs, wallet activity, and token balances. You subscribe and get pushed updates with no polling.
Foundational REST API: historical, decoded data across 100+ chains (transactions v3, logs, balances, etc.). Use it for backtests, PnL attribution, and audits.
In Web3, an AI agent isn’t just a model; it’s a program that perceives on-chain state, makes decisions based on a policy, and executes transactions with a bounded identity (an EOA or smart account). The chain provides verifiability (every action is signed and auditable), composability (it can call any contract it’s authorised to), and accountability (including permissions, limits, and pause controls).
Most practical agents run off-chain loops that watch live data and submit on-chain intents when rules are met. The “AI” part can be as simple as a statistical signal or as involved as an LLM planner; what matters is that decisions are reproducible, logged, and constrained by risk guards.
In this tutorial, an AI agent is a loop that:
Subscribes to live market/chain signals on Base (e.g., OHLCV for a pair).
Score those signals with a transparent predictive rule (stat/ML).
Acts via smart-contract calls only if guards pass (fees, slippage, exposure caps).
You can integrate an LLM planner (e.g., LangChain.js or LangGraph) for multi-step tasks, which will be covered in later sections.
Where does GoldRush fit?
GoldRush is the data plane that enables the agent to “see” in real-time and “remember” accurately, even with sub-second events, allowing the loop to react to fresh signals instead of polling. For backtests, attribution, and audits, the Foundational API provides decoded historical data (transactions v3, logs, balances, traces) so you can measure what the agent actually did and why.
Beyond trading, you can automate risk by pausing or throttling when volatility or netflows spike, running inventory-aware market making (widen spreads as volatility or gas rises), and catching anomalies like unusual wallet bursts or contract call patterns for operations and governance. GoldRush’s streams supply the real-time signals these workflows need.
Structure the agent as a small set of responsibilities with clear interfaces. This keeps decisions explainable, failures easy to isolate, and upgrades (including LLM planning) low-risk. These parts include:
1) Stream Ingestion (real-time)
The primary task here is to maintain a single WebSocket connection to the GoldRush stream and normalize events into a compact envelope that your code understands. No model logic is needed here, just parsing, clocking, and handing off. Pipe these into a bounded ring buffer (e.g., 1–5 minutes of ticks). If the stream hiccups, you drop the oldest entry first and emit a “data_stale” flag (more on this in subsequent sections), so the rest of the system knows to stand down.
2) Feature Store
Converts raw events into lightweight features that the policy can consume. These include: rolling returns and volatility, liquidity depth/impact estimates, wallet net flows, and recent fill status. Version the feature set and snapshot state on a cadence, so any decision is reproducible.
3) Policy Engine
This is the brain, but it is still small. It consumes features and outputs intents with reasons attached. Intents are not transactions; they’re proposals with a confidence score and the exact checks they passed/failed. Later, when you add an LLM planner, it should write the same Intent shape so you can A/B the logic without touching execution.
4) Transaction Pipeline
A narrow service that turns a valid Intent into a signed transaction. It handles:
Idempotency: hash(intent) as a key; refuse duplicates within a window.
Quoting & slippage limits: fetches quotes just-in-time; aborts on drift.
Timeouts & backoff: If a transaction isn’t mined quickly enough, cancel or reduce its size.
Dry-run mode: same path, but it logs instead of sending—great for canaries.
5) Observability & Ledger
Record inputs, features, intent, reason codes, fees, fills, and realized PnL deltas in an append-only store. Publish live health metrics (hit rate, PnL per gas, stale-feed ratio, cancel rate). This layer serves as the basis for reviews, incident analysis, and model updates.
6) Safety layer
This is the global kill switch and the set of hard limits, including notional caps, maximum transactions per minute, allow-lists for routers/contracts, and the staleness interlock from the Eyes. When any guard trips, new intents are quarantined until conditions clear or a human approves an override.
A single process can host all six for small bots, but the interfaces let you scale: you can put the Ingestor on its own worker, shard by pair/wallet key, or swap the ring buffer for a queue. The boundaries stay the same, which keeps your debugging short and your future upgrades painless.
Prerequisites
Node 18+ and a package manager (npm/yarn/pnpm). The GoldRush SDK recommends Node v18 or newer.
RPC access for Base: use a provider for Base Mainnet (chainId 8453) or Base Sepolia (chainId 84532). For production, please refer to the base node providers' docs.
Wallet & keys: a funded EOA/smart account (start on Base Sepolia to test safely).
GoldRush API key for Streaming + Foundational.
Packages: @covalenthq/client-sdk, ethers, and (if rolling your own WS) graphql-ws. The SDK manages WebSocket connections for Streaming.
Install:
npm i @covalenthq/client-sdk ethers graphql-ws
npm i -D typescript tsx @types/node
Create .env:
GOLDRUSH_API_KEY=your_key
BASE_RPC_URL=your_provider_url
PAIR_ADDRESSL=0xYourBasePoolOrPairContract
BOT_WALLET_PK=0xabc... # test key; never commit
Before we dive into the actual code, it’s worth aligning on what you’re actually streaming, how GoldRush delivers it, and what “good” looks like in production.
What “OHLCV” means in this context
OHLCV stands for Open, High, Low, Close, Volume—a candlestick that aggregates many swaps into a single bar. On Base, these bars are derived from DEX pools (e.g., Uniswap v3). Each bar represents a fixed window (e.g., 1 minute) for a specific pool/pair contract.
GoldRush’s OHLCV Pairs stream expects a pool/pair contract address, not a token address. The pool address is typically listed on the DEX pool page, and it's a good idea to keep a small map of token addresses/decimals (such as WETH, USDC, or any other token you’re working with) for later trading logic.
How the stream behaves
When you connect, you choose an interval and a timeframe. That initial backfill warms up your model, so you don’t start cold. Then, you switch to the live bar when you’re ready.
The GoldRush SDK multiplexes multiple subscriptions over a single WebSocket, allowing you you add Token Balances or Wallet Activity streams later without opening new sockets. Keep the event handler light—do heavy work off-thread if you scale up.
Interval = the size of each candle (e.g., ONE_MINUTE).
Timeframe = how many historical bars to send immediately on connect (e.g., one_hour gives ~60 bars).
Staying correct over time (ordering, staleness, replay)
You may occasionally see reconnects and duplicated or slightly out-of-order bars. Deduplicate by bar timestamp, and ignore out-of-order updates older than the last processed minute. Consider data stale if the latest bar is older than approximately twice your interval, and do not act until fresh bars are available.
Finally, some production hygiene to carry out:
Use a real provider RPC URL in .env (check Base docs for official list), avoid the public, rate-limited endpoint.
Confirm pool addresses on an explorer,
Call unsubscribe() on shutdown to avoid dangling sockets, and
Store only a bounded window (e.g., the last 60 bars) to keep memory predictable.
1
src/stream.ts:
2
3
npm i -D tsx
and try again.4
[goldrush] connected
. Then one line per minute response as shown below:npm run dev
5
GOLDRUSH_API_KEY
in .env.Now that the stream is live, we’ll add a tiny, deterministic predictive loop: compute a rolling z-score on 1-minute returns and emit a per-bar signal (with a staleness guard). This remains simple by design and serves as the input to the cost gate in Part 2.
What is a z-score?
A z-score turns the latest log return into a standardized value relative to recent history:z = (r_t − μ) / σ
, where r_t = ln(close_t / close_{t−1})
, and μ/σ
are the rolling mean and standard deviation of returns over a fixed window (e.g., the last 60 one-minute bars).
Keep the window size fixed during a run for determinism; when σ ≈ 0 (indicating a flat market), treat the signal as neutral (z = 0). Anchor calculations to the bar timestamp from the stream and log (bar_time, z, stale) so every decision is replayable.
Why does this fit?
With GoldRush OHLCV, you already have clean, minute-bucketed candles on Base. That means no tick deduping or pool math in your model, push each candle, update μ/σ
, and you have a consistent, latency-aware signal. Base’s fast confirmations make “reactive” strategies more viable; the z-score gives you a deterministic way to detect breakouts/regime shifts without heavy ML.
How it connects with the further parts
In Part 2, you’ll map |z|
to an expected edge (bps), compare that to fees + slippage + a safety buffer, and only act when the inequality clears. In Part 3, you’ll record each (features → checks → action → PnL
) in a decision ledger.
1
2
3
npm run dev
4
You’ve got the core loop running: a clean OHLCV stream on Base, a deterministic z-score over 1-minute returns, and a simple staleness guard so your agent only “thinks” on fresh data. It’s small on purpose—easy to reason about, trivial to replay, and ready to plug into execution logic without surprises.
Swap PAIR_ADDRESS to a second pool and let both run for ~60 bars—watch how z behaves in different regimes.
Nudge WINDOW_BARS (e.g., 60 → 120) and Z_THRESHOLD (e.g., 2.0 → 2.5) to see how sensitivity changes.
Skim your logs: do the timestamps line up, does the stale flip as expected, and are the signals reproducible?
When you’re satisfied the signal is stable, jump to Part 2: Cost-Gated Execution on Base. We’ll fetch firm quotes, convert fees and slippage to basis points (bps), enforce the one inequality that guards every trade, send transactions with Ether, and initiate a PnL backfill to verify results.