// docs

Attack WebSocket Message

View raw Markdown

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:

  • Standard attack — the attack does not touch a known protected address.
  • Protected-address attack — the attack touches an address the recipient has registered for protection. The payload carries three extra fields (victim_protocol_id, victim_protocol, victim_label).

Top-level schema

{ "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 reference

FieldTypeDescription / example values
networkstringSource chain. One of mainnet, bsc, arbitrum, polygon, optimism, base, avax, hyperliquid.
severitystring | nullRisk level. Typical values: "HIGH", "MEDIUM", "LOW", "CRITICAL". May be null when severity could not be computed.
attack_typestringType of detection that fired. See Attack types.
transaction_hashstring (0x + 64 hex)The transaction the alert is bound to.
exploit_addressstring (0x + 40 hex)Contract being interacted with (the suspected exploit / target).
block_numberintBlock height containing the transaction.
block_timestampintUnix epoch seconds, e.g. 1731609600.
proc_timestringUTC timestamp of when the alert was generated, e.g. "2026-05-13 09:42:11.503127".
attacker_addressstring (0x + 40 hex)EOA / contract that initiated the suspected attack.
inputstringRaw transaction calldata, e.g. "0xa9059cbb0000...".
balance_changefloat | nullNet USD value moved by the attacker for this transaction, e.g. 248173.42. Can be 0.0 or negative; null when not computed.
matched_tracesstringComma-separated trace identifiers that matched the detection rules. Often "".
matched_logsstringComma-separated log identifiers that matched. Often "".
matched_selectorsstringComma-separated 4-byte selectors that matched, e.g. "0xa9059cbb,0x23b872dd". Often "".
victim_addressstring | nullAddress suffering the largest loss in USD; may be null for attack types without a single identifiable victim.
protocolsobject | nullEnrichment payload — see Enriched data.
victim_protocol_idint (protected-address variant only)Defimon-internal numeric id of the protocol that owns the matched protected address, e.g. 42. Distinct from the DeBank protocol_id nested inside protocols — see Two different protocol ids.
victim_protocolstring (protected-address variant only)Human-readable protocol name, e.g. "Aave V3".
victim_labelstring (protected-address variant only)Label associated with the protected address, e.g. "Aave V3 Pool".

Two different protocol ids

The payload carries two identifiers with confusingly similar names. They come from different systems and are not interchangeable:

victim_protocol_id (top level)protocol_id (nested in protocols)
Typeintstring
SourceDefimon's own protected-address registryDeBank API
Example42"aave3", "bsc_venus"
Present whenThe attack touched an address registered for protectionThe address resolved to a known DeBank protocol
AnswersWhich of my protected protocols was hit?Which public DeFi protocol did DeBank match this address to?

Do not join one against the other, and do not parse the DeBank slug as a number — it is an opaque string, chain-prefixed on every chain except Ethereum.

Attack types

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_profit
  • highly_complex_transaction_with_profit
  • mev_tx_with_unusual_profit
  • exploit_in_initcode
  • abnormal_token_minting
  • suspicious_large_transfer
  • access_transfer_to_suspicious_address
  • access_transfer_to_suspicious_address_with_profit
  • malicious_proxy_upgrade
  • proxy_upgrade_frontrun (CPIMP attack)

Enriched data (protocols)

The protocols field carries enrichment about the victim and the addresses involved in the transaction. Three shapes are possible:

1. null

No enrichment was attached to this alert. The field is emitted as JSON null.

2. Lightweight enrichment

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": "aave3" } }

3. Full enrichment

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": "aave3" }, "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": "aave3", "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
FieldTypeExample
is_eoaboolfalse
addressstring"0x3333...3333"
namestring | null"Aave V3", "Unverified contract", or null
symbolstring | null"aUSDC", "USDC", or null
tvlnumber | nullUSD TVL when known; null otherwise.
is_coingecko_poolbooltrue when the address was classified as a CoinGecko-tracked liquidity pool.
is_protocolbooltrue for known protocols.
site_urlstring | nullProtocol site URL, e.g. "https://aave.com".
protocol_idstring | nullDeBank protocol id — a string slug, not a number. Unprefixed on Ethereum (e.g. "aave3"), prefixed with the DeBank chain id elsewhere (e.g. "bsc_venus", "matic_aave3"). This is not the same identifier as the top-level victim_protocol_id — see Two different protocol ids. null when the victim is not a known DeBank protocol; on lightweight enrichment the key may be absent rather than null, so read it with a safe accessor.
balance_changes[] entry fields

Each 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):

FieldTypeWhen present
addressstringAlways (may be "" for synthetic protocol-summary rows).
balance_change_usdnumberAlways. Negative = loss, positive = gain. Synthetic protocol-summary rows use 1 as a sentinel.
is_eoaboolAlways.
namestring | nullSet unless the address is an EOA. "Unverified contract" for unresolved contracts.
symbolstring | nullToken symbol when known.
tvlnumber | nullProtocol/pool TVL when known.
site_urlstringKnown protocols only.
protocol_idstringDeBank protocol matches only, e.g. "aave3". Same slug format as victim_info.protocol_id. Omitted for CoinGecko pools, Etherscan-named contracts, unresolved contracts, and for the synthetic protocol-summary rows (address: "") — those carry name/tvl/site_url but no id.
is_protocolboolNon-EOA entries.
is_coingecko_poolboolSet to true only on CoinGecko pool matches.
raw_token_balances

Maps owner_address → { token_address: signed_amount_as_string }. Amounts are raw on-chain integers (un-decimalled) and may be negative strings.

token_prices.token_data

Maps "{network}:{token_address_lower}" to { price, decimals, symbol }, where price and decimals are strings.

End-to-end example

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": "aave3" }, "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": "aave3", "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" }

Confirmed-attack feed

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:

  • Endpoint: /ws/confirmed_attacks (same API-key authentication as /ws/attacks).
  • Subscription: to receive these over Telegram or to filter by them, use the alert type 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.
  • Timing: confirmed attacks arrive after the regular alert (the LLM round-trip takes from a few seconds up to a couple of minutes). Not every attack produces a confirmed attack — false positives are dropped.

Payload

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:

FieldTypeDescription
llm_explanationstringLLM-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_idint | nullDefimon-internal numeric protocol id of the matched protected address, or null. Same identifier as on the /ws/attacks feed — still unrelated to the DeBank protocol_id nested in protocols.
victim_protocolstring | nullHuman-readable protocol name, or null.
victim_labelstring | nullLabel 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.

Put the feed in your stack

Connect in minutes. Every attack across major chains, as JSON, in under a second.

@DefimonAlerts

© 2026 Defimon by Decurity

Powered by QuickNode