Skip to content

feat(decoder): Bancor V3 BancorNetwork trade() decoder + protocol tagging - #157

Merged
0xfandom merged 1 commit into
developfrom
feat/mempool-bancor-decoder
May 25, 2026
Merged

feat(decoder): Bancor V3 BancorNetwork trade() decoder + protocol tagging#157
0xfandom merged 1 commit into
developfrom
feat/mempool-bancor-decoder

Conversation

@0xfandom

Copy link
Copy Markdown
Collaborator

Summary

  • Adds Protocol::BancorV3 enum variant + Bancor V3 BancorNetwork calldata decoder
  • Covers both trade flavours: tradeBySourceAmount (commit source, accept min target) + tradeByTargetAmount (commit target, pay max source)
  • Bancor router address (0xeEF417e1D5CC832e619ae18D2F140De2999dD4fB) joins default_router_addresses — Alchemy now actually forwards Bancor traffic to us
  • PROTOCOL_BANCOR writer wire label registered so the schema stays pinned
  • Pipeline tags decoded Bancor swaps via protocol="bancor_v3" on every mempool metric
  • Post-state scan integration intentionally deferred (same pattern as the Curve PR feat(decoder): Curve pool-direct exchange() decoder + protocol tagging #156) — kept reviewable

Why

Bancor V3 has been in our ProtocolType enum + BancorPool predictor for months, but the decoder rejected every Bancor calldata path with UnknownSelector. This unblocks the observability for Bancor's BNT-intermediary swap volume — small relative to Uni V2/V3 but real and currently invisible.

Files Changed

File Purpose
crates/pools/src/router_decoder.rs Protocol::BancorV3 variant, IBancorNetwork sol! interface, try_bancor dispatch, 2 unit tests
crates/ingestion/src/mempool.rs Bancor router address in default_router_addresses
crates/grpc-server/src/mempool_writer.rs PROTOCOL_BANCOR wire label constant (paired with the existing reserved PROTOCOL_CURVE)
crates/grpc-server/src/mempool_pipeline.rs Bancor arms in decoder_protocol_to_type, decoder_protocol_label, protocol_label. Early-return at try_post_state_scan top with bancor_post_state_pending skip reason

Implementation notes

  • Router-mediated, not pool-direct. Unlike Curve, Bancor swaps target the single BancorNetwork router — fits the existing decoder model without needing a parallel "pool-direct" code path. token_in and token_out come from calldata directly (sourceToken / targetToken args), no registry resolution needed.
  • Both trade shapes collapse to one DecodedSwap body. tradeByTargetAmount puts the user-committed target amount in DecodedSwap.amount_in and the source ceiling in amount_out_min. Semantics slightly differ from the source-amount path but both numbers are real magnitudes — the upstream predictor gets actionable signal either way.
  • fee_bps = 0 for Bancor in the decoded swap. Bancor's fee is applied per-leg through the BNT intermediary and isn't visible in calldata; the predictor reads the on-chain fee from pool state.

Scope cut

Same as Curve PR #156. The follow-up PR needs:

  • predict_post_state_with_fallback Bancor arm (BancorPool currently has no analytical post-state hook through the unified fallback API)
  • PredictedPostState::Bancor { reserve_in, reserve_out } variant in mempool_writer
  • unified_to_post_reserves Bancor arm in pipeline

Splitting keeps each review surface ~150 LOC instead of ~500.

Acceptance criteria

  • tradeBySourceAmount decodes with correct token + amount
  • tradeByTargetAmount decodes with target as amount_in, max source as amount_out_min
  • Bancor swaps tag aether_pending_dex_tx_total{protocol="bancor_v3"} instead of decode_failure
  • aether_mempool_predictions rows persist with protocol="bancor" once writer is reached (writer constant pinned, pipeline label wired)
  • Bancor post-state cycles surface on aether_pending_arb_candidates_total (follow-up)

Test plan

  • cargo build --release clean
  • cargo clippy --all-targets -- -D warnings clean
  • cargo test --workspace --release -- --test-threads=1 all green
  • 2 unit tests in router_decoder::tests::decode_bancor_* covering both trade shapes
  • go build ./... + go test ./... -count=1 green (no Go changes, regression check)
  • Verify against live mainnet — aether_pending_dex_tx_total{protocol="bancor_v3"} should increment within an hour (Bancor volume is lower than Uniswap; expect ≥ 1 per few-min window)

@vercel

vercel Bot commented May 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aether Ready Ready Preview, Comment May 25, 2026 7:01pm
aether-63xv Ready Ready Preview, Comment May 25, 2026 7:01pm

@0xfandom
0xfandom marked this pull request as ready for review May 25, 2026 12:15
@0xfandom

Copy link
Copy Markdown
Collaborator Author

Live mainnet smoke (10-min, ran on this branch)

===== protocol=bancor_v3 hits =====
(empty)

===== bancor_post_state_pending skips =====
(empty)

===== baseline decode hits by protocol =====
aether_pending_dex_tx_total{decoded="false",protocol="unknown",router="0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45"} 6
aether_pending_dex_tx_total{decoded="false",protocol="unknown",router="0x7a250d5630b4cf539739df2c5dacb4c659f2488d"} 7
aether_pending_dex_tx_total{decoded="true",protocol="sushiswap",router="0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f"} 1
aether_pending_dex_tx_total{decoded="true",protocol="uniswap_v2",router="0x7a250d5630b4cf539739df2c5dacb4c659f2488d"} 29
aether_pending_dex_tx_total{decoded="true",protocol="uniswap_v3",router="0xe592427a0aece92de3edee1f18e0157c05861564"} 6

protocol="bancor_v3" count is 0 in this 10-min window. Different cause than the Curve smoke — the Bancor router address (0xeEF417e1D5CC832e619ae18D2F140De2999dD4fB) is in default_router_addresses now, so Alchemy will forward Bancor traffic to us. The window simply saw no tradeBySourceAmount / tradeByTargetAmount traffic. Bancor V3 mainnet volume is currently ~$1-5M/day vs. Uni V2's ~$200M+/day, so multi-minute gaps between trades are expected.

What this validates

  • Build is clean against live Alchemy WS for 10 consecutive minutes
  • New decoder + new router address don't crash the pipeline (binary ran full window, clean shutdown)
  • No regressions in existing protocol decode hits (29 V2 + 6 V3 + 1 Sushi still flowing)
  • Zero bancor_unsupported decode-error events — no false-positive matches against non-Bancor calldata
  • Router address is wired (would otherwise have been logged as protocol="unknown" against 0xeEF417… — no such row appeared)

What it does NOT validate

  • A real Bancor trade* calldata round-trip through the decoder — needs a longer observation window or capture+replay of a historical Bancor tx

Recommendation

Merge as-is. Confidence the decoder works comes from the 2 unit tests against real ABI shapes (decode_bancor_trade_by_source_amount + decode_bancor_trade_by_target_amount) plus the clean live run. Will run a longer (1-2 hr) observation post-merge and post counts as a follow-up note if useful.

Adds Bancor V3 to the mempool decoder coverage matrix. Unlike Curve,
Bancor swaps go through a single router (`BancorNetwork` at
0xeEF417e1...), so the existing router-filter model fits without
modification — Bancor's router address joins the default filter and
both trade flavours decode cleanly:

* `tradeBySourceAmount(sourceToken, targetToken, sourceAmount,
  minReturnAmount, deadline, beneficiary)` — most common shape
* `tradeByTargetAmount(sourceToken, targetToken, targetAmount,
  maxSourceAmount, deadline, beneficiary)` — surfaces the target
  amount as `amount_in` so the predictor has a real magnitude

Same scope cut as the Curve PR: decoder + protocol tagging now, post-
state scan integration deferred (the existing `BancorPool` predictor
needs a `predict_post_state_with_fallback` hook + a Bancor variant on
`PredictedPostState` + `unified_to_post_reserves` before cycles can
flow through the simulator).

* `Protocol::BancorV3` enum variant
* `PROTOCOL_BANCOR` writer wire label (paired with the existing
  `PROTOCOL_CURVE` reserved constant)
* Bancor router added to `default_router_addresses` so Alchemy
  actually delivers these txs
* Pipeline arms across `decoder_protocol_to_type`,
  `decoder_protocol_label`, `protocol_label`, plus early-return at
  `try_post_state_scan` with `bancor_post_state_pending` skip reason
* 2 unit tests covering both trade shapes
@0xfandom
0xfandom force-pushed the feat/mempool-bancor-decoder branch from cc38bbb to d449ec1 Compare May 25, 2026 19:00
@0xfandom
0xfandom merged commit ba1e3b2 into develop May 25, 2026
3 checks passed
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.

1 participant