Skip to content

Fix int256 overflow in token imbalances from spam transfer amounts - #380

Closed
data-cowwboy wants to merge 1 commit into
mainfrom
fix/imbalances-int256-overflow
Closed

Fix int256 overflow in token imbalances from spam transfer amounts#380
data-cowwboy wants to merge 1 commit into
mainfrom
fix/imbalances-int256-overflow

Conversation

@data-cowwboy

Copy link
Copy Markdown
Contributor

Problem

The raw slippage query (4059683) fails with Overflow in INT256 cast of UINT256: 115792089237316...639935 for any BNB time window containing 2026-08-22 (reported in Slack).

Root cause: a spam token (0xf0e8fc6211feb9a1ea9f94609c245bc39973e4d7 on BNB) emitted a fake Transfer event of 2^256 − 1 atoms to the settlement contract in tx 0xf06378e10b27ccc35de3c8c2ba53fc6da2a5c837d9be5b10a584ab24dfa8a710 — a direct EOA→token call, not a settlement; there is no corresponding row in cow_protocol_bnb.trades. The token imbalances query (4021644) does cast(amount as int256) on every transfer amount, and max-uint256 does not fit into int256.

Fix

Ignore transfer amounts ≥ 2^255 in the imbalances query. Such amounts cannot come from real trades (no token supply comes anywhere near 2^255), so this only drops spam events that would otherwise crash the query. Filtering is preferred over try_cast, which would silently turn the amount into NULL inside the sum.

Testing

Tested on Dune with copies of the production queries:

  • Fixed imbalances copy (8425681) completes on the failing window (bnb, 2026-08-18 → 2026-08-25): 44,952 rows.
  • End-to-end raw slippage copy referencing it (8425704) completes on the same window: 86,069 rows. The unmodified query fails with the INT256 overflow on these parameters.
  • Regression check on a window without the spam event (bnb, 2026-08-18 → 2026-08-22, raw_slippage_per_transaction): original vs fixed produce identical tx sets (11,292), all slippage_wei values identical, slippage_usd differs only by float summation-order noise (max 2.8e-14).

The two noqa: PRS markers cover sqlfluff parse errors from the non-standard int256/uint256 types (same convention as classified_fees_4058574.sql and balance_changes_4021257.sql); sqlfluff lint now passes on the file, whereas it already failed on main for the pre-existing int256 cast block.

🤖 Generated with Claude Code

A spam token on BNB emitted a fake Transfer event of 2^256 - 1 atoms to the
settlement contract (tx 0xf06378e10b27ccc35de3c8c2ba53fc6da2a5c837d9be5b10a584ab24dfa8a710,
2026-08-22). Casting that amount to int256 overflows and makes the raw
slippage query fail for any time window containing the event. Amounts of
2^255 and larger cannot come from real trades and are now ignored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
from "query_4021257(blockchain='{{blockchain}}',start_time='{{start_time}}',end_time='{{end_time}}')"
-- Spam tokens emit fake Transfer events with amounts of up to 2^256 - 1, which do not fit into
-- int256. Amounts of 2^255 and larger cannot come from real trades and are ignored.
where amount < uint256 '57896044618658097711785492504343953926634992332820282019728792003956564819968' --noqa: PRS, LT02

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use POWER(2, 255) instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested it, power() is not safe here because it works in doubles:

  1. power(2, 255) returns a double, so amount < power(2, 255) coerces the uint256 amount to double before comparing. Near 2^255 adjacent doubles are 2^202 apart, so the comparison is only approximate at the boundary (e.g. uint256 '2^255 - 1' < power(2, 255) evaluates to false even though that value fits int256).
  2. The precise-looking variant cast(power(2, 255) as uint256) actually evaluates to 578960446186581000...0, which is about 2.3e60 larger than the real 2^255. Amounts in that gap would pass the filter and still crash the int256 cast.

Verification of both: https://dune.com/queries/8427355

An exact alternative would be bitwise_left_shift(uint256 '1', 255) (verified bit-identical to the literal in https://dune.com/queries/8427357), but that seemed harder to read than the plain constant, so I kept the literal with the comment explaining it.

@harisang

Copy link
Copy Markdown
Contributor

Haven't read the PR but my first reaction would be the following. Wouldn't it be better if we just exclude that particular tx hash, so as to not silently fail on potential other instances of this?

@bram-vdberg

Copy link
Copy Markdown
Contributor

Addressed by #381

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants