This document describes the JSON structure of WebSocket messages delivered to clients when an on-chain attack is detected.
Messages are delivered on two feeds:
/ws/attacks — every detected attack, delivered immediately. This is the
feed described in the bulk of this document./ws/confirmed_attacks — a curated, lower-noise stream of attacks that
have been post-processed and confirmed as genuine exploits by an LLM
pipeline. See Confirmed-attack feed. A single client
may subscribe to both feeds at the same time.Each message is a single JSON object. There are two variants of the payload:
victim_protocol_id, victim_protocol, victim_label).{
"network": "mainnet",
"severity": "HIGH",
"attack_type": "suspicious_contract_call_with_profit",
"transaction_hash": "0x9c8b6f3b6f6a1b2a3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b",
"exploit_address": "0x1111111111111111111111111111111111111111",
"block_number": 21345678,
"block_timestamp": 1731609600,
"proc_time": "2026-05-13 09:42:11.503127",
"attacker_address": "0x2222222222222222222222222222222222222222",
"input": "0xa9059cbb000000000000000000000000abcdef...",
"balance_change": 248173.42,
"matched_traces": "",
"matched_logs": "",
"matched_selectors": "",
"victim_address": "0x3333333333333333333333333333333333333333",
"protocols": { "...see Enriched data below..." },
"victim_protocol_id": 137,
"victim_protocol": "Aave V3",
"victim_label": "Aave V3 Pool"
}The last three fields (victim_protocol_id, victim_protocol,
victim_label) are present only in the protected-address variant. For a
standard attack they are absent from the JSON document.
| Field | Type | Description / example values |
|---|---|---|
network | string | Source chain. One of mainnet, bsc, arbitrum, polygon, optimism, base, avax, hyperliquid. |
severity | string | null | Risk level. Typical values: "HIGH", "MEDIUM", "LOW", "CRITICAL". May be null when severity could not be computed. |
attack_type | string | Type of detection that fired. See Attack types. |
transaction_hash | string (0x + 64 hex) | The transaction the alert is bound to. |
exploit_address | string (0x + 40 hex) | Contract being interacted with (the suspected exploit / target). |
block_number | int | Block height containing the transaction. |
block_timestamp | int | Unix epoch seconds, e.g. 1731609600. |
proc_time | string | UTC timestamp of when the alert was generated, e.g. "2026-05-13 09:42:11.503127". |
attacker_address | string (0x + 40 hex) | EOA / contract that initiated the suspected attack. |
input | string | Raw transaction calldata, e.g. "0xa9059cbb0000...". |
balance_change | float | null | Net USD value moved by the attacker for this transaction, e.g. 248173.42. Can be 0.0 or negative; null when not computed. |
matched_traces | string | Comma-separated trace identifiers that matched the detection rules. Often "". |
matched_logs | string | Comma-separated log identifiers that matched. Often "". |
matched_selectors | string | Comma-separated 4-byte selectors that matched, e.g. "0xa9059cbb,0x23b872dd". Often "". |
victim_address | string | null | Address suffering the largest loss in USD; may be null for attack types without a single identifiable victim. |
protocols | object | null | Enrichment payload — see Enriched data. |
victim_protocol_id | int (protected-address variant only) | Protocol ID of the matched protected address, e.g. 42. |
victim_protocol | string (protected-address variant only) | Human-readable protocol name, e.g. "Aave V3". |
victim_label | string (protected-address variant only) | Label associated with the protected address, e.g. "Aave V3 Pool". |
Possible values of attack_type for network-wide exploit monitoring (i.e. alerts that are not scoped
to a specific protected address), subscribe to the following set of
attack_type values:
suspicious_contract_call_with_profithighly_complex_transaction_with_profitmev_tx_with_unusual_profitexploit_in_initcodeabnormal_token_mintingsuspicious_large_transferaccess_transfer_to_suspicious_addressaccess_transfer_to_suspicious_address_with_profitprotocols)The protocols field carries enrichment about the victim and the addresses
involved in the transaction. Three shapes are possible:
nullNo enrichment was attached to this alert. The field is emitted as JSON null.
Returned when only victim information is available:
{
"is_full_enrichment": false,
"victim_info": {
"is_eoa": false,
"address": "0x3333333333333333333333333333333333333333",
"name": "Aave V3",
"symbol": "aUSDC",
"tvl": 4123456789.12,
"is_coingecko_pool": false,
"is_protocol": true,
"site_url": "https://aave.com",
"protocol_id": 42
}
}Returned for high-impact alerts and contains, in addition to victim_info,
the full set of address-level balance changes for the transaction:
{
"is_full_enrichment": true,
"hacker_profit": 248173.42,
"victim_info": {
"is_eoa": false,
"address": "0x3333333333333333333333333333333333333333",
"name": "Aave V3",
"symbol": "aUSDC",
"tvl": 4123456789.12,
"is_coingecko_pool": false,
"is_protocol": true,
"site_url": "https://aave.com",
"protocol_id": 42
},
"balance_changes": [
{
"address": "0x2222222222222222222222222222222222222222",
"balance_change_usd": 248173.42,
"is_eoa": true
},
{
"address": "0x3333333333333333333333333333333333333333",
"balance_change_usd": -248173.42,
"is_eoa": false,
"name": "Aave V3",
"symbol": "aUSDC",
"tvl": 4123456789.12,
"site_url": "https://aave.com",
"protocol_id": 42,
"is_protocol": true
},
{
"address": "0x4444444444444444444444444444444444444444",
"balance_change_usd": -1532.10,
"is_eoa": false,
"name": "USDC/WETH 0.05%",
"symbol": "USDC",
"tvl": 123456789.0,
"is_coingecko_pool": true,
"is_protocol": false
},
{
"address": "0x5555555555555555555555555555555555555555",
"balance_change_usd": -42.0,
"is_eoa": false,
"name": "Unverified contract",
"symbol": null,
"tvl": null,
"is_protocol": false
},
{
"address": "",
"balance_change_usd": 1,
"is_eoa": false,
"name": "Uniswap V3",
"symbol": null,
"tvl": 5000000000.0,
"site_url": "https://uniswap.org",
"is_protocol": true
}
],
"raw_token_balances": {
"0x2222222222222222222222222222222222222222": {
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "248173420000"
},
"0x3333333333333333333333333333333333333333": {
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "-248173420000"
}
},
"token_prices": {
"token_data": {
"ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": {
"price": "1.00",
"decimals": "6",
"symbol": "USDC"
},
"ethereum:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2": {
"price": "3450.12",
"decimals": "18",
"symbol": "WETH"
}
}
}
}victim_info fields| Field | Type | Example |
|---|---|---|
is_eoa | bool | false |
address | string | "0x3333...3333" |
name | string | null | "Aave V3", "Unverified contract", or null |
symbol | string | null | "aUSDC", "USDC", or null |
tvl | number | null | USD TVL when known; null otherwise. |
is_coingecko_pool | bool | true when the address was classified as a CoinGecko-tracked liquidity pool. |
is_protocol | bool | true for known protocols. |
site_url | string | null | Protocol site URL, e.g. "https://aave.com". |
protocol_id | int | null | Numeric protocol id, e.g. 42. |
balance_changes[] entry fieldsEach entry always includes address, balance_change_usd, and is_eoa.
The remaining fields depend on how the address was classified (known
protocol, CoinGecko pool, verified contract, or unresolved):
| Field | Type | When present |
|---|---|---|
address | string | Always (may be "" for synthetic protocol-summary rows). |
balance_change_usd | number | Always. Negative = loss, positive = gain. Synthetic protocol-summary rows use 1 as a sentinel. |
is_eoa | bool | Always. |
name | string | null | Set unless the address is an EOA. "Unverified contract" for unresolved contracts. |
symbol | string | null | Token symbol when known. |
tvl | number | null | Protocol/pool TVL when known. |
site_url | string | Known protocols only. |
protocol_id | int | Known-protocol matches only. |
is_protocol | bool | Non-EOA entries. |
is_coingecko_pool | bool | Set to true only on CoinGecko pool matches. |
raw_token_balancesMaps owner_address → { token_address: signed_amount_as_string }. Amounts
are raw on-chain integers (un-decimalled) and may be negative strings.
token_prices.token_dataMaps "{network}:{token_address_lower}" to { price, decimals, symbol },
where price and decimals are strings.
WebSocket payload for an attack on a tracked protected address with full enrichment:
{
"network": "mainnet",
"severity": "HIGH",
"attack_type": "suspicious_contract_call_with_profit",
"transaction_hash": "0x9c8b6f3b6f6a1b2a3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b",
"exploit_address": "0x1111111111111111111111111111111111111111",
"block_number": 21345678,
"block_timestamp": 1731609600,
"proc_time": "2026-05-13 09:42:11.503127",
"attacker_address": "0x2222222222222222222222222222222222222222",
"input": "0xa9059cbb0000000000000000000000003333333333333333333333333333333333333333000000000000000000000000000000000000000000000000000000003b9aca00",
"balance_change": 248173.42,
"matched_traces": "",
"matched_logs": "",
"matched_selectors": "0xa9059cbb",
"victim_address": "0x3333333333333333333333333333333333333333",
"protocols": {
"is_full_enrichment": true,
"hacker_profit": 248173.42,
"victim_info": {
"is_eoa": false,
"address": "0x3333333333333333333333333333333333333333",
"name": "Aave V3",
"symbol": "aUSDC",
"tvl": 4123456789.12,
"is_coingecko_pool": false,
"is_protocol": true,
"site_url": "https://aave.com",
"protocol_id": 42
},
"balance_changes": [
{
"address": "0x2222222222222222222222222222222222222222",
"balance_change_usd": 248173.42,
"is_eoa": true
},
{
"address": "0x3333333333333333333333333333333333333333",
"balance_change_usd": -248173.42,
"is_eoa": false,
"name": "Aave V3",
"symbol": "aUSDC",
"tvl": 4123456789.12,
"site_url": "https://aave.com",
"protocol_id": 42,
"is_protocol": true
}
],
"raw_token_balances": {
"0x2222222222222222222222222222222222222222": {
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "248173420000"
},
"0x3333333333333333333333333333333333333333": {
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": "-248173420000"
}
},
"token_prices": {
"token_data": {
"ethereum:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48": {
"price": "1.00",
"decimals": "6",
"symbol": "USDC"
}
}
}
},
"victim_protocol_id": 42,
"victim_protocol": "Aave V3",
"victim_label": "Aave V3 Pool"
}Attacks of certain types are additionally post-processed by an LLM pipeline
(the /explain-hack command). When that pipeline confirms a transaction is a
genuine exploit (as opposed to a false positive such as legitimate MEV /
arbitrage or normal protocol activity), the service emits a confirmed
attack on a separate feed:
/ws/confirmed_attacks (same API-key authentication as
/ws/attacks).confirmed_attack in your subscription alerts list. This is
independent of the underlying detection type, so you can subscribe to the
curated confirmed stream without subscribing to the raw attack types.The confirmed-attack payload is a strict superset of the standard attack
payload — every field documented above is present with the same name and type,
so existing /ws/attacks consumers can parse a confirmed-attack message with
their current parser and ignore the extra fields. The confirmed payload adds:
| Field | Type | Description |
|---|---|---|
llm_explanation | string | LLM-generated, Telegram-ready report describing the exploit: affected protocol, loss amount, token/price, vulnerability type, a short description, and TX / victim links. Always present. |
victim_protocol_id | int | null | Protocol ID of the matched protected address, or null. |
victim_protocol | string | null | Human-readable protocol name, or null. |
victim_label | string | null | Label of the protected address, or null. |
Note the difference from the /ws/attacks feed: there, the three
victim_protocol_* fields are omitted for standard attacks and only appear
in the protected-address variant. On /ws/confirmed_attacks they are always
present, defaulting to null when the attack does not touch a protected
address — so the confirmed feed has a single, uniform shape. Detect a
protected-address confirmed attack with a null check
(victim_protocol_id != null) rather than key presence.
The attack_type field retains the underlying detection type (e.g.
suspicious_contract_call_with_profit); the confirmed_attack label is used
only for subscription/feed routing, not as the value of attack_type.
In DeFi, a small delay costs millions.
Get the threat intelligence to rely on.