AI-Driven DeFi Exploit Detection with GoldRush (Part 1) — From exploit stories to real-time signals

Joseph Appolos
Content Writer
Part 1 of a 3-part guide: build a real-time DeFi exploit monitor on Base using GoldRush Streams, with wallet-based alerts that prepare you for AI detection.

Why DeFi still feels fragile

Most DeFi incident timelines look depressingly similar. A protocol grows. TVL climbs, governance stabilises, the token finds a home in dashboards and “safe” portfolios. Then, suddenly, there’s a message in a Telegram group or on X:

“..... has been hacked, X Million drained. We’re working on it. Do not deposit.”

What follows is familiar: emergency tweets, a pause on the UI, a post-mortem with transaction screenshots and function traces. Somewhere in the middle is the uncomfortable realisation that, on-chain, the whole sequence was visible as it unfolded:

  • Liquidity drained from one or two pools over a handful of blocks.

  • Ownership or upgrade rights shifted to a new address.

  • A contract started receiving calls that had never appeared in production traffic before.

Those aren’t unknowable mysteries. They’re data streams that nobody treated as a security surface. This series is about closing that gap for teams and power users who already think in data:

  • We’ll use GoldRush as the data rail for both historical and real-time on-chain information.

  • We’ll use AI as the reasoning layer that learns patterns and flags behaviour that deserves attention.

  • And we’ll show how to go from “reading post-mortems” to running an early-warning agent that watches live DeFi telemetry.

This part of the guide (Part 1)  is the foundation. We’ll map the landscape of DeFi exploits, look at the numbers, clarify where AI is actually beneficial, and position GoldRush as the infrastructure layer that makes this possible. The second half of Part 1 will then turn that context into a concrete telemetry service. Parts 2 and 3 will layer on learning and alerting.

How DeFi actually gets exploited

DeFi exploit” is a broad label. From a protocol’s perspective, most incidents fall into a few recurring patterns.

  1. Smart-contract and protocol flaws

These are failures in the protocol’s own design or implementation:

  • Re-entrancy and call-order bugs — The state is updated in the wrong order, or not fully updated before external calls, allowing repeat withdrawals.

  • Price and oracle manipulation — protocols assume a DEX price or TWAP is “good enough,” while an attacker uses flash liquidity to bend that price and borrow against fake collateral.

  • Logic and access-control errors — missing checks allow arbitrary callers to trigger admin-grade actions or bypass risk constraints.

These are the exploits that show up in detailed write-ups from security firms. They also leave a clear footprint: unusual function calls, repeated patterns within a single transaction, and state changes that don’t match normal usage.

  1. Liquidity, bridges, and market structure

Some failures are less about bugs and more about how a protocol is wired into DeF. We see them mostly asi:

  • A governance token with thin liquidity is accepted as collateral.

  • A lending market trusts a single bridge or oracle.

  • Incentive changes cause liquidity to migrate faster than the risk engine can handle.

On-chain, these episodes appear as sharp movements in balances and reserves: one-sided flows, TVL collapsing in a handful of blocks, or liquidity exiting just after a parameter change.

  1. Governance and admin-key misuse

A significant share of serious incidents have been governance or admin failures, not pure technical bugs. Some of the scenarios we’ve seen in the past are when:

  • A proxy is upgraded to a malicious implementation.

  • A multisig is compromised, or signer thresholds are set too low.

  • A governance proposal smuggles in dangerous changes under a benign title.

These often happen shortly before liquidity starts to move.

  1. User-level compromise and phishing

The last category sits at the wallet layer. It includes the following:

  • Phishing pages that trick users into signing malicious approvals.

  • Drainer contracts that batch-transfer assets out of many addresses.

  • Malware or keyloggers are stealing private keys.

In the first half of 2025, for example, Certik estimated nearly $2.5B in losses from scams and hacks, with about $1.71B attributed to compromised wallets and more than $410M to phishing attacks.

These aren’t “DeFi protocol bugs,” but they still show up as unusual outflows and approval patterns.

In this series, we’ll focus mainly on protocol-level and liquidity-level behaviour—the parts where early detection can help protocols and serious users spot trouble before it forms.

The scale of the problem

Zooming out helps explain why better observability and detection are worth the engineering time.

A few anchor points from recent years:

  • In 2022, crypto hackers stole roughly $3.8B, with DeFi protocols responsible for a large share of those losses.

  • In 2023, total stolen funds fell to around $1.7B, and the value lost in DeFi protocol hacks dropped by roughly 63.7% year-on-year, but still sat firmly in the billions.

  • According to Immunefi, 2024 year-to-date has already seen more than $1.21B lost to hacks and rug pulls across 150+ incidents, with losses about 15.5% higher than the same period in 2023.

  • In some ecosystems, like BNB Chain, cumulative losses since inception are estimated at around $1.64B, including $1.27B from hacks and $368M from fraud.

Across different datasets, one pattern stands out: in many periods, over 90% of recorded losses come from hacks rather than pure fraud, indicating the dominant risk remains technical and structural, not only social engineering.

So the question isn’t whether DeFi is “safe” in theory. It’s how much of this risk could have been spotted earlier if we treated on-chain behaviour as a live security surface, not just something to reconstruct after an incident.

How these incidents show up on-chain

Once you strip away the narrative from individual hacks, you’re left with a small set of recurring, machine-readable symptoms. From a data perspective, most of the categories above can be reduced to combinations of a few observable patterns. Below is a simple view of the exploits:

What changes between incidents isn’t the basic vocabulary of events. It’s the combination and timing:

  • Owner changes, then a proxy upgrade, then liquidity movement.

  • Thin liquidity, then a significant price swing, then abnormal borrowing.

  • A quiet governance token, then concentrated voting and a controversial proposal.

This is precisely the kind of pattern recognition task that benefits from real-time data and, eventually, AI.

Where AI is beneficial in DeFi security 

There’s a lot of generic “AI for security” noise right now. For our purposes, we’ll keep the scope narrow and concrete. In a DeFi early-warning system, AI is most useful in three places:

  1. Anomaly detection: Models learn what “normal” looks like from time-series features—liquidity, transfer volume, holder concentration, admin-event frequency—and flag segments that deviate in statistically meaningful ways.

  2. Exploit signature learning: Historical incidents become signatures: specific combinations of events and state changes in defined windows. Models help generalise those signatures across chains and protocols, so similar patterns are easier to catch elsewhere.

  3. Alert prioritisation: A naïve system fires too many alerts. AI can cluster and rank them by context—address reputation, cross-contract correlations, similarity to past incidents—so humans see a focused queue instead of a firehose.

For that reason, this series treats AI as a layer on top of deterministic telemetry, not a replacement for explicit rules or human review. And that brings us to the data rail we’ll build on.

GoldRush as the data backbone for AI security agents

GoldRush is the infrastructure layer we’ll lean on throughout this series. It doesn’t decide what’s safe or unsafe; it makes the raw material/data available in a consistent, machine-friendly way.

Two products matter most for our use case.

Historical context with the Foundational API

GoldRush’s Foundational API gives you historical blockchain data—transactions, decoded logs, balances, holders, NFTs—through a unified REST interface and SDKs across 100+ chains. For security work, that means you can rebuild the full before/after context of past exploits as datasets, turn that history into feature sets for anomaly detection and exploit-signature models, and compare protocol behaviour across chains using the same schema, all without running your own indexer for every chain.

Real-time visibility with the Streaming API

The Streaming API is the real-time side. It provides sub-second access to on-chain events via a GraphQL-over-WebSocket interface so that you can subscribe to:

  • Token transfers

  • DEX activity and OHLCV data

  • New pairs and liquidity changes

  • Wallet movements and other decoded events

In practice, that’s the backbone of our early-warning agent:

  • A small TypeScript service connects to GoldRush Streams.

  • It subscribes to events for a set of contracts and addresses (for example, a protocol’s pools on Base).

  • It maintains a compact state per contract—liquidity, owner, recent large transfers—and emits a continuous risk “heartbeat.”

  • Later, models and more advanced logic plug into that heartbeat instead of talking to nodes directly.

Because the historical and streaming APIs share a consistent model, the features we design from past data are the same ones we can compute in real time.

Why we’ll use Base as our reference network

The architecture we’re describing is chain-agnostic. To keep the examples concrete, we’ll use Base as the reference network for this series:

  • It’s EVM-compatible, so DeFi patterns and tooling carry over cleanly.

  • It has active liquidity and agents, which makes the telemetry interesting.

  • GoldRush supports it, so both Foundational and Streaming APIs are available for it.

In the second side of Part 1, we’ll start building that glue:

  • Set up a small TypeScript service running against GoldRush’s Streaming API on Base.

  • Track liquidity, ownership changes, and large transfers for a handful of contracts.

  • Emit a simple, human-readable “risk heartbeat” that updates every second.

That will give us a clean, real-time signal layer. In Part 2, we’ll use that layer—and historical data from GoldRush—to train and evaluate exploit signatures and anomaly detectors that turn raw logs into meaningful warnings.

Step-by-Step Tutorial: Build the Part-1 Exploit Signal Monitor

What you’ll build in Part 1

In this part, you’ll ship a small, always-on service that:

  • Connects to GoldRush Streaming API on Base mainnet.

  • Subscribes to wallet activity for a set of “watch” addresses (e.g., governance multisig, treasury, top LPs).

  • Labels incoming transactions with simple risk signals (large transfers, approvals, watch-wallet involvement).

  • Prints one readable summary per suspicious transaction, with a severity level.

This is your data-plane skeleton: clean, real-time signals that later parts will feed into anomaly models and AI scoring, instead of you trying to bolt AI onto a messy log stream.

Prerequisites

You can follow this on Windows, macOS, or Linux.

  • Node.js 18+ (20 recommended)

  • Git (optional but helpful)

  • A code editor (VS Code is fine)

A GoldRush API key (from the GoldRush dashboard or via a provider like QuickNode’s GoldRush Wallet API add-on).

Project architecture for Part 1

The Part-1 project is a small TypeScript service that connects to GoldRush’s Streaming API on Base, watches activity for a set of wallets, applies simple risk heuristics to each transaction, and prints a live security feed you can extend with richer logic in later parts.

For this first part, keep the layout small and focused:

1

Set up the project

In your terminal, paste the following code:
mkdir defi-exploit-signals-part1 cd defi-exploit-signals-part1 npm init -y # Runtime deps npm install @covalenthq/client-sdk ws dotenv zod # Dev deps (TypeScript tooling) npm install --save-dev typescript ts-node @types/node # Create tsconfig.json npx tsc --init
This gives you a minimal TypeScript project and installs the GoldRush SDK, along with WebSocket support for Node.
2

Configure secrets and thresholds (.env)

Create a .env file in the project root:
touch .env
Paste into the .env:
COVALENT_API_KEY=your_goldrush_api_key_here # Comma-separated list of watch wallets (treasury, governance multisig, LPs, etc.) WATCH_WALLETS=0xYourGovernanceWallet,0xYourTreasuryWallet # Heuristics for Part 1 (USD values, approximate) LARGE_TX_USD=50000 SUSPICIOUS_APPROVAL_USD=10000
What these do
  • COVALENT_API_KEY – lets the SDK authenticate with GoldRush.
  • WATCH_WALLETS – any transaction involving one of these addresses is treated as “interesting”.
  • LARGE_TX_USD – threshold for flagging “whale-sized” transfers.
  • SUSPICIOUS_APPROVAL_USD – threshold for approvals that should get extra scrutiny.
In later parts, you’ll complement these heuristics with learned anomaly scores; for now, they give you a practical, explainable baseline.
3

Central config loader (src/config.ts)

What this step does
  • Loads .env.
  • Validates required fields (API key, watch wallets).
  • Normalises watch wallets to lowercase.
  • Exposes thresholds via a single CONFIG object used everywhere else.
Create src/config.ts and paste the code below:
// src/config.ts import 'dotenv/config'; import { z } from 'zod'; const EnvSchema = z.object({ COVALENT_API_KEY: z.string().min(1, 'COVALENT_API_KEY is required'), WATCH_WALLETS: z.string().min(1, 'WATCH_WALLETS is required'), LARGE_TX_USD: z.coerce.number().optional(), SUSPICIOUS_APPROVAL_USD: z.coerce.number().optional(), }); const env = EnvSchema.parse(process.env); const largeTxUsd = env.LARGE_TX_USD ?? 50_000; const suspiciousApprovalUsd = env.SUSPICIOUS_APPROVAL_USD ?? 10_000; export const CONFIG = { apiKey: env.COVALENT_API_KEY, watchWallets: env.WATCH_WALLETS.split(',') .map((a) => a.trim().toLowerCase()) .filter((a) => a.length > 0), thresholds: { largeTxUsd, suspiciousApprovalUsd, }, }; if (CONFIG.watchWallets.length === 0) { throw new Error('WATCH_WALLETS must contain at least one address'); }
What to expect
You don’t run this file directly. Other modules import CONFIG:
  • CONFIG.apiKey – GoldRush API key.
  • CONFIG.watchWallets – normalised list of addresses.
  • CONFIG.thresholds.largeTxUsd,
  • CONFIG.thresholds.suspiciousApprovalUsd.
If you typo a variable in .env, the process will throw a readable error on startup instead of failing silently later.
4

Label suspicious activity (src/signals.ts)

This file turns a raw transaction from the stream into a small, human-readable security signal.
For Part 1, we keep the rules intentionally simple:
  • Flag large transfers above LARGE_TX_USD.
  • Flag approvals, especially high-value ones.
  • Only alert if the tx involves a watch wallet, unless the amount is extreme.
Create src/signals.ts and paste the code below:
// src/signals.ts import { CONFIG } from './config'; export type Severity = 'info' | 'medium' | 'high'; export type SecuritySignal = { severity: Severity; reasons: string[]; tags: string[]; // e.g. ["large_transfer", "approval", "from_watch_wallet"] }; type StreamTx = { tx_hash?: string; from_address?: string; to_address?: string; block_signed_at?: string; timestamp?: string; decoded_type?: string; decoded?: { quote_usd?: number | null; [key: string]: unknown; }; [key: string]: unknown; }; export function analyseTx(tx: StreamTx): SecuritySignal | null { const reasons: string[] = []; const tags: string[] = []; const valueUsd = typeof tx.decoded?.quote_usd === 'number' ? tx.decoded.quote_usd : undefined; const decodedType = tx.decoded_type ?? ''; const watchSet = new Set(CONFIG.watchWallets); const from = tx.from_address?.toLowerCase() ?? ''; const to = tx.to_address?.toLowerCase() ?? ''; const involvesWatch = watchSet.has(from) || watchSet.has(to); // Heuristic 1: large transfers if (valueUsd !== undefined && valueUsd >= CONFIG.thresholds.largeTxUsd) { reasons.push(`Large transfer ≈ $${valueUsd.toFixed(0)}`); tags.push('large_transfer'); } // Heuristic 2: approvals (often abused in phishing/compromise flows) if (decodedType.toUpperCase().includes('APPROVAL')) { if ( valueUsd !== undefined && valueUsd >= CONFIG.thresholds.suspiciousApprovalUsd ) { reasons.push( `High-value approval (${decodedType}) around $${valueUsd.toFixed(0)}` ); tags.push('approval'); } else { reasons.push(`Approval transaction (${decodedType})`); tags.push('approval'); } } // If nothing stands out yet, but it touches a watch wallet, still surface it. if (involvesWatch) { if (!reasons.length) { reasons.push('Activity involving watch wallet'); } if (watchSet.has(from)) tags.push('from_watch_wallet'); if (watchSet.has(to)) tags.push('to_watch_wallet'); } // If we neither touch a watch wallet nor hit any rule, ignore it for now. if (!involvesWatch && reasons.length === 0) { return null; } let severity: Severity = 'info'; if (tags.includes('large_transfer')) { severity = 'high'; } else if ( tags.includes('approval') && valueUsd !== undefined && valueUsd >= CONFIG.thresholds.suspiciousApprovalUsd ) { severity = 'high'; } else if (tags.includes('approval')) { severity = 'medium'; } return { severity, reasons, tags }; }
What to expect
Again, you won’t run this file directly. When stream.ts calls analyseTx(tx):
  • It returns null if nothing interesting was found.
  • Otherwise, it returns a small object like the image below:
Note: This is deliberately simple and easy to explain. In Part 2, you’ll augment these labels with statistical features (burstiness, unusual patterns vs baseline) and feed them into AI models.
5

Subscribe to GoldRush wallet activity (src/stream.ts)

This section wires up src/stream.ts, the module that handles all the plumbing for the monitor. It creates a GoldRushClient with your API key, subscribes to the Wallet Activity Stream for your chosen watch wallets on Base mainnet, normalises the different payload shapes returned by the stream (whether a single transaction object or a walletTxs array), and then feeds each transaction to analyseTx.
Whenever a transaction triggers one of your heuristics, the module prints a single, concise summary line you can treat as your live security feed.
Create src/stream.ts: and paste the code below:
// src/stream.ts import WebSocket from 'ws'; import { GoldRushClient, StreamingChain, } from '@covalenthq/client-sdk'; import { CONFIG } from './config'; import { analyseTx, SecuritySignal } from './signals'; // GoldRush SDK expects a WebSocket implementation in Node. (global as any).WebSocket = WebSocket; let client: GoldRushClient | null = null; let unsubscribe: (() => void) | null = null; type AnyTx = { tx_hash?: string; from_address?: string; to_address?: string; block_signed_at?: string; timestamp?: string; decoded_type?: string; decoded?: { quote_usd?: number | null; [k: string]: unknown }; [k: string]: unknown; }; function shortAddress(addr?: string | null): string { if (!addr) return 'n/a'; return `${addr.slice(0, 6)}…${addr.slice(-4)}`; } function logSignal(tx: AnyTx, sig: SecuritySignal) { const ts = tx.block_signed_at || tx.timestamp || new Date().toISOString(); const from = shortAddress(tx.from_address); const to = shortAddress(tx.to_address); const valueUsd = typeof tx.decoded?.quote_usd === 'number' ? tx.decoded.quote_usd : undefined; const headlineParts = [ `[${ts}]`, `${from} → ${to}`, sig.tags.length ? `[${sig.tags.join(',')}]` : '', valueUsd !== undefined ? `≈ $${valueUsd.toFixed(0)}` : '', ].filter(Boolean); console.log(headlineParts.join(' ')); console.log( ` ↳ Severity: ${sig.severity} | ${sig.reasons.join('; ')}` ); } function handleTx(raw: AnyTx) { if (!raw?.tx_hash) return; const sig = analyseTx(raw); if (!sig) return; logSignal(raw, sig); } export async function startStream() { if (!client) { client = new GoldRushClient( CONFIG.apiKey, {}, { onConnecting: () => console.log('[stream] Connecting to GoldRush…'), onOpened: () => console.log('[stream] Connected'), onClosed: () => console.log('[stream] Disconnected'), onError: (err) => console.error( '[stream] Error:', (err as any)?.message || String(err) ), } ); } console.log( `[stream] Watching ${CONFIG.watchWallets.length} wallet(s) on Base mainnet` ); CONFIG.watchWallets.forEach((w) => console.log(` - ${w}`) ); // Wallet Activity Stream subscription unsubscribe = client.StreamingService.subscribeToWalletActivity( { chain_name: StreamingChain.BASE_MAINNET, wallet_addresses: CONFIG.watchWallets, }, { next: (data: any) => { // Node examples sometimes wrap txs in data.data.walletTxs[] if (Array.isArray(data?.data?.walletTxs)) { data.data.walletTxs.forEach((tx: AnyTx) => handleTx(tx)); return; } // React examples often emit a single tx object directly if (data && (data as AnyTx).tx_hash) { handleTx(data as AnyTx); return; } // Fallback: log unexpected shapes for debugging console.log('[stream] Unexpected payload shape'); }, error: (err: any) => console.error( '[stream] Subscription error:', err?.message || String(err) ), complete: () => console.log('[stream] Stream completed'), } ); } export async function stopStream() { if (unsubscribe) { unsubscribe(); unsubscribe = null; } if (client) { await client.StreamingService.disconnect(); client = null; } }
What to expect
When you start the app, you’ll see connection logs similar to the image below:
When a relevant transaction lands, you’ll see output in this style:
[2025-11-27T09:15:32Z] 0x1234…abcd → 0x9fed…0011 [large_transfer,from_watch_wallet] ≈ $120000 ↳ Severity: high | Large transfer ≈ $120000; Activity involving watch wallet
If nothing matches your heuristics yet, the process will stay quiet. That’s expected on a quiet wallet set.
6

Wire up the primary process (src/index.ts)

Finally, create a small entry point that starts the stream and cleanly shuts it down.
Create src/index.ts:
// src/index.ts import { startStream, stopStream } from './stream'; async function main() { console.log('DeFi Exploit Signal Monitor — Part 1'); console.log('From exploit stories to real-time signals\n'); await startStream(); process.on('SIGINT', async () => { console.log('\n[main] Caught SIGINT, shutting down…'); await stopStream(); process.exit(0); }); } main().catch((err) => { console.error('[main] Fatal error:', err); process.exit(1); });
Run it and inspect the output.
In your terminal:
npm run dev
If everything is wired correctly, you should see:
DeFi Exploit Signal Monitor — Part 1 From exploit stories to real-time signals [stream] Watching 2 wallet(s) on Base mainnet - 0xyourgov… - 0xyourtreasury… [stream] Connecting to GoldRush… [stream] Connected
Once monitored wallets start interacting with DeFi:
  • Large transfers or approvals that exceed your thresholds appear as high-severity lines.
  • Smaller approvals and generic activity involving watch wallets show up as medium/info lines.
You can tune LARGE_TX_USD and SUSPICIOUS_APPROVAL_USD in .env without changing code. Over time, this becomes your “early smoke” dashboard while you work on more sophisticated models.

Common errors and quick fixes

A few issues you might see in Part 1:

  • COVALENT_API_KEY is required → Check .env spelling and that the file is in the project root. Ensure npm run dev is executed from that directory.

  • WATCH_WALLETS must contain at least one address → Confirm you have at least one comma-separated address with no stray newlines.

  • [stream] Unexpected payload shape spam  → This means the payload didn’t match either data.data.walletTxs or a top-level tx object. It’s safe to ignore temporarily; you can log JSON.stringify(data, null, 2) while debugging.

No signals at all → Your watch wallets might be idle, or the thresholds might be too high. Lower the USD thresholds temporarily or point to a more active governance/treasury wallet.

Wrapping Up

Where we are, and what’s next

In the first section of this part — Part 1, Side 1 — we looked at how DeFi exploits actually occur and how they surface on-chain. Here, you turned that theory into a minimal, concrete monitor.

By the end of this second side of Part 1, you now have:

  • A real-time GoldRush stream on Base mainnet watching addresses you care about.

  • A clean, isolated config layer for secrets and thresholds.

  • A single, composable signal function that turns raw wallet activity into simple risk labels.

  • A console view that converts exploit stories into machine-readable, time-stamped events you can later feed into AI.

In Part 2, we’ll:

  • Extend this monitor with historical context from the GoldRush Foundational API (e.g., baselines for “normal” behaviour).

  • Turn your current heuristics into features (burstiness, ratios, anomaly scores).

  • Prepare datasets and wiring so an AI model can sit alongside this stream and help you prioritise alerts.

Get Started

Get started with GoldRush API in minutes. Sign up for a free API key and start building.

Support

Explore multiple support options! From FAQs for self-help to real-time interactions on Discord.

Contact Sales

Interested in our professional or enterprise plans? Contact our sales team to learn more.