Skip to content

fix(dex): guard 0x settler volume_usd against mismatched maker leg#9795

Open
karimhass wants to merge 1 commit into
mainfrom
fix/zeroex-settler-leg-divergence-volume
Open

fix(dex): guard 0x settler volume_usd against mismatched maker leg#9795
karimhass wants to merge 1 commit into
mainfrom
fix/zeroex-settler-leg-divergence-volume

Conversation

@karimhass

Copy link
Copy Markdown
Contributor

Summary

The 0x settler decoder (zeroex_v2 macro, all 17 chains) matches the maker (bought) leg heuristically. In bundled / multi-path settler transactions (e.g. Relay-routed orders), it can latch onto an unrelated side-swap transfer, so volume_usd collapses to a tiny, wrong amount.

Reported example — tx 0xf61374…bf57: a ~$499 RLUSD→cbBTC trade was reported as $0.75, because the maker leg was matched to a parallel RLUSD→USDC fee hop (0.750073 USDC) instead of the real cbBTC output.

sold: RLUSD 499.25 ($499.35)   bought: USDC 0.750073 ($0.75)   divergence 666x
volume_usd: $0.75  ->  $499.35

Fix

Add a leg-divergence guard in zeroex_v2_trades_detail: when both priced legs diverge by ≥ leg_divergence_tolerance (5×), anchor volume_usd on the deterministic sold (taker) leg — the user's actual settler input, matched first/most reliably — instead of the mismatch-prone maker leg.

  • The taker/sold leg is the reliable anchor; the maker/output leg is where the heuristic mis-binds.
  • Fixes understatement (tiny mismatched maker, the reported case) and a smaller set of overstatement cases (maker matched to a large intermediate hop, e.g. WETH→wstETH inflated ~3000×).
  • All non-divergent rows are byte-identical (only the final volume_usd expression changes, via ELSE amount_usd).
  • Deliberately does not touch the shared add_amount_usd macro or the fragile maker/taker matching logic.

Scope / impact (Ethereum, 60d; cross-chain is larger)

status trades old volume new volume
unchanged ~948k $5.1467B $5.1467B (identical)
corrected up (understated) ~2.6k $1.81M $97.6M (+$95.8M)
corrected down (overstated) ~1.1k $5.18M $0.40M (−$4.8M)

Cross-chain (all 17 chains, 30d): ~93.9k clearly-divergent (≥10×) 0x trades, ~$82.7M of understated volume.

Notes / follow-up

Test plan

  • dbt compile -s zeroex_v2_ethereum_trades passes; guard renders with taker_amount/maker_amount resolving.
  • Regression simulated on prod zeroex_v2_ethereum.trades (60d): consistent population unchanged to the cent; divergent rows corrected toward the sold-leg value.
  • Flagged tx 0xf61374… resolves $0.75 → $499.35.
  • CI dbt slim ci builds the modified zeroex_v2_*_trades models across chains.

Made with Cursor

The 0x settler decoder (zeroex_v2 macro) matches the maker (bought)
leg heuristically. In bundled / multi-path settler txs (e.g. Relay-routed
orders) it can latch onto an unrelated side-swap transfer, so volume_usd
collapses to a tiny amount (e.g. tx 0xf61374...bf57 reported $0.75 for a
~$499 RLUSD->cbBTC trade, mirroring a parallel RLUSD->USDC fee hop).

Add a leg-divergence guard: when both priced legs diverge by >= 5x, anchor
volume_usd on the deterministic sold (taker) leg -- the user's actual
settler input -- instead of the mismatch-prone maker leg. This fixes both
the understatement (tiny mismatched maker) and a smaller set of
overstatement cases (large intermediate-hop maker), and leaves all
non-divergent rows byte-identical.

Regression (ethereum, 60d): ~948k consistent rows unchanged to the cent;
~2.6k understated rows corrected up (+$95.8M); ~1.1k overstated rows
corrected down (-$4.8M). Does not re-derive the wrong maker_token columns;
that is a tracked follow-up.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions github-actions Bot marked this pull request as draft June 15, 2026 15:04
@github-actions github-actions Bot added WIP work in progress dbt: dex covers the DEX dbt subproject labels Jun 15, 2026
@karimhass karimhass marked this pull request as ready for review June 15, 2026 15:05
@github-actions github-actions Bot added ready-for-review this PR development is complete, please review and removed WIP work in progress labels Jun 15, 2026
thevaizman added a commit that referenced this pull request Jun 16, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 16, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 16, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 17, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 17, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 17, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 17, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 17, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 17, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 17, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 19, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 19, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thevaizman added a commit that referenced this pull request Jun 19, 2026
…9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
tomfutago added a commit that referenced this pull request Jun 23, 2026
… (CUR2-2844) (#9800)

* feat(dex): add 0x Settler RFQ fills to dex.trades (robust echo-ported decoder, 16 chains)

Promotes 0x Settler plain-RFQ (action selector 0xd92aadfb) maker fills into dex.trades
as a new PMM venue (project '0x API', version 'settler') across 16 chains. They were
previously absent from dex.trades (which only carried the legacy ExchangeProxy decodes).

Method, ported from Dune's echo indexer (zero_ex_settler.rs):
- Token identities (maker, makerAsset, takerToken) come from the signed RFQ action's
  calldata static head; amounts come from the real ERC20 Transfer logs pivoted on the
  maker, with an exactly-one-per-leg validity gate (drops ambiguous / native-ETH legs).
- Identity-grouped maker pivot so distinct fills sharing a byte offset across multiple
  settler traces in one tx are not collapsed (the naive (tx_hash,p) grouping silently
  dropped/aliased such fills via arbitrary()).

Files:
- new macro zeroex_settler_rfq + per-chain zeroex_settler_<chain>_base_trades (16 chains),
  registered in dex_<chain>_base_trades; dex_info + ethereum seed test added.
- settler-txs staging extended to emit tx_from + rfq_input (RFQ-bearing rows only).

Verified on-chain (Ethereum + sampled chains): decoder reproduces real fills exactly;
0 overlap with the legacy native 0x venue (no double counting); AMM-routed settler
trades correctly excluded (their underlying pool venues already represent them).

mode excluded (no dex pipeline). dex_aggregator path unchanged in this PR.

CUR2-2843

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* perf(dex): bound dex_base_trades CI full-refresh to 7d to avoid OOM/timeout

CI slim-builds full-refresh dex_<chain>_base_trades (the duplicates_rank window over
a chain's entire history), exceeding the per-node memory limit on large chains
(bnb: 331GB TopN vs 348GB ceiling) and the CI job timeout. Bound the union to the
last 7 days in CI only (target=ci); prod (dunesql) is unchanged and keeps the
incremental_predicate. Verified no CI test depends on full-history dex_base_trades:
aggregate models carry no history-dependent tests (only dex_story's
check_dex_info_relationship, which passes on a subset), seed tests query venue models
(not the aggregate), and dex_<chain>_trades is out of CI scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(dex): dedup 0x Settler RFQ rows on (tx_hash, evt_index)

The 0xd92aadfb selector can recur inside an RFQ action's ABI body, so the
all-occurrences scan decodes the SAME action twice (identical maker/asset/token)
at two byte offsets, both matching the one maker-leg transfer -> duplicate
(tx_hash, evt_index). Keep one row per (tx_hash, maker_evt_index) via row_number;
genuine distinct fills always have distinct maker-leg logs, so only the spurious
re-decode is dropped (verified: the lone ethereum 14d collision was an
identical-tuple recurrence at offsets 1093/1125 of tx 0x49da06f1...). Uses a
subquery rather than QUALIFY (no precedent for QUALIFY in this codebase).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(dex): deterministic 0x Settler aggregator volume — supersede PR #9795 (CUR2-2844)

Replaces the heuristic maker/taker leg-matching (and the PR #9795 5x-divergence band-aid)
for 0x Settler trades in dex_aggregator.trades with a deterministic decode. Per settler call,
emits one 0x-API aggregator row = the user's net swap:

- token_bought = AllowedSlippage.buyToken (from calldata, deterministic).
- amount = the unique Transfer(buyToken, to=receiver) when resolvable, else the minAmountOut
  floor (verified tight, ~1% under actual). receiver = tx-level sender (execute) / msgSender
  (executeMetaTxn) — verified to be the true buyToken recipient, NOT the calldata recipient
  (an intermediate routing hop). Anchoring on a single calldata-named token to the verified
  user means it cannot mis-bind to internal routing hops the way the heuristic did.
- token_sold = best-effort (unique Transfer out of the user, token != buyToken).
- volume_usd priced via either leg (add_amount_usd), so an unpriced buyToken still values off
  the sell leg (cuts the unpriced-NULL bucket ~20% -> ~5%).

New macro zeroex_settler_agg; settler-txs staging extended with the AllowedSlippage fields
(buy_token, min_amount_out, settler_msgsender); all 17 settler chains' zeroex_v2_<chain>_trades
rewired to it.

Verified on-chain (example 0xf61374… : current $0.75 -> deterministic ~$490). NOTE: requires a
coordinated full-refresh of zeroex_v2_<chain>_trades + dex_aggregator_trades (merge cannot delete
the old mis-bound rows). Old-vs-new regression to be validated on CI-built tables before merge.

Stacked on CUR2-2843 (Phase 1). CUR2-2844

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: tomfutago <tomfutago@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dbt: dex covers the DEX dbt subproject ready-for-review this PR development is complete, please review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant