feat(dex): wire 1inch-LOP aggregator trades into dex_aggregator.trades (CUR2-2693, 2/2) - #9759
Conversation
…gregator wiring Re-land of #9747 (reverted in #9752) — part 1 of 2. The revert's root cause was the base-tier wiring: generic enrich_dex_aggregator_trades divides raw amounts by tokens.erc20.decimals and recomputes USD, but this lineage's raw amounts are not erc20-consistent (executed-transfer amounts can be denominated in a different token — the $48T audit rows; and the complement fill amounts are order-level mis-scaled on some fills — a $78 USDT/SFUND fill implying $235M, verified live). Neither raw source survives generic enrichment, so the view now serves fully-enriched values incl. 1inch's own guarded amount_usd, exactly like oneinch_ar_trades (full-history audit gate: 0 rows > $1B on all 13 chains, max $38.3M). This PR intentionally leaves the view UNWIRED (dex_aggregator_base_trades restored byte-identical to main; dex_aggregator_trades untouched) so the dex.trades dedup — the actual CUR2-2693 bug — lands without rebuilding the 739M-row table; the one-line as_is hookup follows in part 2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s (CUR2-2693, 2/2) One-line as_is hookup of oneinch_lop_aggregator_trades (introduced unwired in part 1). The view serves 1inch's own enriched values incl. its guarded amount_usd — full-history audit gate: 0 rows > $1B on all 13 chains, max $38.3M — so the accepted_range failure that reverted #9747 cannot recur via generic enrichment (this PR's CI rebuilds dex_aggregator_trades with these rows and runs that exact test pre-merge). Stacked on #9758; merge only after it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PR SummaryHigh Risk Overview A shared Reviewed by Cursor Bugbot for commit e2bec45. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Six-hour merge race double-count
- Delayed oneinch_lop_own_trades until fills age out of the venue classifier window and extended the passthrough incremental overlap so newly mature rows still merge once.
Or push these changes by commenting:
@cursor push 3659104cd6
Preview (3659104cd6)
diff --git a/dbt_subprojects/dex/macros/models/oneinch_lop_dex_trades_passthrough.sql b/dbt_subprojects/dex/macros/models/oneinch_lop_dex_trades_passthrough.sql
--- a/dbt_subprojects/dex/macros/models/oneinch_lop_dex_trades_passthrough.sql
+++ b/dbt_subprojects/dex/macros/models/oneinch_lop_dex_trades_passthrough.sql
@@ -46,7 +46,9 @@
AND o.block_date > current_date - interval '3' day -- dev_dates mode for dev, to prevent full scan
{%- else -%}
{% if is_incremental() %}
- AND {{ incremental_predicate('o.block_time') }}
+ -- oneinch_lop_own_trades only emits after classifier finality; keep a one-unit
+ -- overlap so newly mature rows are still picked up by dex_<blockchain>_trades.
+ AND {{ incremental_predicate("o.block_time + interval '1' " ~ var('DBT_ENV_INCREMENTAL_TIME_UNIT')) }}
{% endif %}
{%- endif %}
diff --git a/dbt_subprojects/dex/models/_projects/oneinch/outside/oneinch_lop_own_trades.sql b/dbt_subprojects/dex/models/_projects/oneinch/outside/oneinch_lop_own_trades.sql
--- a/dbt_subprojects/dex/models/_projects/oneinch/outside/oneinch_lop_own_trades.sql
+++ b/dbt_subprojects/dex/models/_projects/oneinch/outside/oneinch_lop_own_trades.sql
@@ -10,7 +10,6 @@
{% set src_symbol = "coalesce(src_executed_symbol, '')" %}
{% set dst_symbol = "coalesce(dst_executed_symbol, '')" %}
{% set placeholder_tokens = oneinch_cross_chain_placeholder_tokens_cfg_macro() | join(', ') %}
-{% set maturity_delay = "interval '6' hour" %}
@@ -70,6 +69,6 @@
)
-- maturity delay: the 1inch lineage builds from raw traces while venue base trades
-- build from decoded events; a fill merged into dex_<blockchain>_trades before its
- -- venue's event decodes would never be removed (merge can't delete), so fills only
- -- pass through once old enough for the venue side to have landed
- and f.block_time <= now() - {{ maturity_delay }}
+ -- venue classification finalizes would never be removed (merge can't delete), so
+ -- fills only pass through once they age out of the classifier's incremental window
+ and not ({{ incremental_predicate('f.block_time') }})You can send follow-ups to the cloud agent here.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit e2bec45. Configure here.
tomfutago
left a comment
There was a problem hiding this comment.
Regression Report: PR #9759
Overall status: PASS / no blocker
Scope: targeted regression for dex_aggregator_trades, focused on the new 1inch-LOP as-is hookup and the prior amount_usd accepted-range failure.
Accepted Range Check
dbt_utils_accepted_range_dex_aggregator_trades_amount_usd__1000000000 passed in CI.
Independent Dune scan of CI relation dune.dune_spellbook_ci__tmp_pr9759_27368340570_1.dex_aggregator_trades:
- Rows with
amount_usd > 1B:0 - Rows with negative
amount_usd:0 - Max
amount_usdoverall:$714.17M 1inch-LOProws withamount_usd > 1B:01inch-LOPmaxamount_usd:$38.35M
1inch-LOP Regression
Relations checked:
- Source:
dune.dune_spellbook_ci__tmp_pr9759_27368340570_1.oneinch_lop_aggregator_trades - Final:
dune.dune_spellbook_ci__tmp_pr9759_27368340570_1.dex_aggregator_trades - Prod:
dex_aggregator.trades
Results:
- CI source
1inch-LOProws:3,881,477 - CI final
1inch-LOProws:3,881,477 - Prod
1inch-LOProws:0as expected pre-merge - Source-only keys:
0 - Final-only keys:
0 - Source duplicate keys:
0 - Final duplicate keys:
0
Note
There are 191 matched rows on 2026-06-11 where the live source view now has amount_usd populated but the materialized CI final table has NULL, totaling $380,378 of source-vs-CI drift. Since keys match exactly and the drift is confined to fresh rows after the CI snapshot, this looks like upstream/view enrichment drift rather than a PR regression.
Conclusion: safe to proceed from regression perspective.


Part 2 of 2 re-landing #9747 (CUR2-2693). Stacked on #9758 — merge only after it.
One line: adds
ref('oneinch_lop_aggregator_trades')to theas_is_modelslist, so venue-settled 1inch-LOP fills (excluded fromdex.tradesby #9758) get their intent-layer record index_aggregator.trades— exactly howoneinch_ar_tradesrows are served today.Why this can't reproduce the #9752 revert
The revert's failure mode was generic erc20-decimals enrichment applied to lineage raw amounts. The as_is path skips enrichment entirely: rows carry 1inch's own enriched amounts and guarded
amount_usd. Full-history validation across all 13 chains: 0 rows with amount_usd > $1B, 0 negative, max $38.3M (top-5 all plausible large fills). Additionally, this PR's own CI rebuildsdex_aggregator_tradeswith these rows and runs the exactdbt_utils_accepted_rangetest that failed in prod — pre-merge proof, not post-deploy discovery.CI expectations
While #9758 is unmerged, this PR's CI builds the full stack (CI compares against the prod S3 manifest, not the base branch): venue_settled (~9 min) + views +
dex_aggregator_tradesfull build (28.6–75 min observed variance for the same build) + tests. After #9758 merges and this branch rebases, the diff collapses to one line and CI builds onlydex_aggregator_trades+ its tests.Post-merge
Full refresh of
dex_aggregator_trades(backfills historical'1inch-LOP'rows), then re-run the audit query — the full-history gate says it passes. Note open PRs #9690/#9357 also touch this file (trivial append rebase for whoever lands second).🤖 Generated with Claude Code