Skip to content

Add 42 UniV2/V3 fork factories as 2 merged extractors#380

Open
Haikane wants to merge 9 commits intomainfrom
feat/add-univ2-v3-fork-factories
Open

Add 42 UniV2/V3 fork factories as 2 merged extractors#380
Haikane wants to merge 9 commits intomainfrom
feat/add-univ2-v3-fork-factories

Conversation

@Haikane
Copy link
Copy Markdown
Contributor

@Haikane Haikane commented Feb 20, 2026

Summary

  • Add substreams support for 39 UniswapV2 fork factories and 3 UniswapV3 fork factories on Ethereum mainnet
  • Single extractor per protocol version: all V2 forks share one extractor (ethereum-univ2-forks.yaml), all V3 forks share one (ethereum-univ3-forks.yaml), reducing indexing load from 42 extractors to 2
  • Multi-factory support in map_pools_created: comma-separated factory addresses with optional :fee suffix for per-factory fee overrides (V2), comma-separated addresses (V3)
  • Backward compatible: single-address params still work as before for existing extractors (uniswap, sushiswap, pancakeswap)
  • Add CLONE_TO_BASE_PROTOCOL mappings for all 42 forks in protocol-testing
  • Add integration test configs with real pool creation data for all factories
  • All forks use uniswap_v2_pool / uniswap_v3_pool protocol types (no changes needed in tycho-simulation or tycho-execution)
  • Fee bounds validation: map_pools_created asserts fee <= 10000 after param deserialization

Params format

V2 (query string with multi-factory support):

factory_address=addr1,addr2:25,addr3:20&protocol_type_name=uniswap_v2_pool

Addresses without :fee suffix use the default fee param (30 bps).

V3 (comma-separated addresses):

"addr1,addr2,addr3"

V3 fees come from the PoolCreated event, so no per-factory fee config needed.

Non-standard fees (3 factories, V2 only)

Factory Name Fee (bps) Verification
0x460b2005... PepeDex 25 Modified pair contract: 25/10000 formula
0x696708db... Linkswap 25 25 bps for LINK pairs, 30 bps for non-LINK (variable per pool)
0xe1046fcb... fork e1046f 20 Custom fee in pair contract

Removed factories (4 total)

  • 2 upgradeable proxies: 0x8ff485ee... and 0x69bd16ae... (TransparentUpgradeableProxy)
  • 1 V3 fork: 0xbaceb8ec... (swap execution reverts)
  • 1 V2 fork: 0x7386f6f2... (no pools created yet, included as placeholder)

Factories evaluated but not included (3)

  • Fraxswap -- TWAMM, not pure constant-product
  • KingSwap -- virtual price manipulation + token locks
  • SafeSwap -- LP token burn on each swap

Test plan

  • WASM build: cargo build --target wasm32-unknown-unknown --release (V2 + V3)
  • Unit tests: cargo test (V2 + V3)
  • cargo check on protocol-testing with new clone mappings
  • Integration tests: all 42 pass with merged YAML configs
  • Fee verification: on-chain contract source analysis for non-standard fee factories
  • Existing base UniV2/V3/Sushiswap/Pancakeswap extractors unaffected (backward compatible params)

🤖 Generated with Claude Code

Haikane and others added 8 commits February 20, 2026 09:42
Add substreams YAML configurations for 44 UniswapV2 fork factories and
4 UniswapV3 fork factories on Ethereum mainnet. These are byte-for-byte
UniV2/V3 code forks deployed through non-standard factory addresses,
discovered through mainnet Swap event monitoring.

All factories verified to emit standard PairCreated/PoolCreated events.
Deployment blocks sourced from Etherscan contract creation data.
No simulation or execution changes needed; pools use existing UniV2/V3
WASM binaries.

Named forks: NineInch, Fraxswap, Linkswap, CroDefi, Oboswap, OrionPool,
YouSwap, JMSwap, KingSwap, Kwikswap, SaitaSwap, HiSwap, PepeDex, Yape,
SakeSwap, SumSwap, SafeSwap, MintyswapV2, BtSwap, ShibaswapV2,
MintyswapV3, BlueprintV3, plus unnamed address-identified forks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix proto file references in all 48 YAML configs (remove non-existent
  entity.proto, match exact proto imports from base configs)
- Add CLONE_TO_BASE_PROTOCOL entries for all 48 forks so the test
  runner knows which base substreams binary to use
- Add integration test configs (47 files) with real pool creation data
  from Etherscan (1 factory has no pools yet)
- Fix V3 test configs to use hardcoded "uniswap_v3_pool" protocol type
  (V3 module doesn't parameterize this like V2 does)

Tested: NineInch V2, Fraxswap V2, ShibaSwap V3 all pass range tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Enable skip_simulation: false for 8 factories across all priority
levels (2 high V2, 2 medium V2, 2 low V2, 2 V3). Results:
- 7/8 pass with simulations enabled
- 1 V3 fork (0xbaceb8...) reverts on swap execution despite having
  liquidity; may have modified swap logic. Reverted to skip_simulation.

All 47/47 factories pass indexing (range) tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Factory 0xbaceb8ec6b9355dfc0269c18bac9d6e2bdc29c4f emits standard
PoolCreated events and indexing works, but its pools revert on swap
execution despite having liquidity. Not a true byte-for-byte V3 clone.

Total: 47 factories -> 46 (44 V2 + 3 V3 remaining, 1 V3 removed).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All V2 forks now use protocol_type_name=uniswap_v2_pool instead of
custom names (e.g., nineinch_v2_pool). This ensures pools are
automatically recognized by the existing simulation and execution
layers without needing registration in tycho-simulation or
tycho-execution.

V3 forks already used the hardcoded uniswap_v3_pool type.

All 46/46 integration tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove TransparentUpgradeableProxy factories that could be upgraded
to produce non-standard pools:
- 0x8ff485eed59e9931fbb984e6d9e8a4b2b459deeb (impl: 0x47db7a...)
- 0x69bd16ae6f507bd3fc9ecc984d50b04f029ef677 (impl: 0xfbacbc...)

Total: 44 factories remaining (42 V2 + 3 V3).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove Fraxswap (TWAMM), KingSwap (virtual price), and SafeSwap
(LP dilution) as they are not pure CPMMs. Add optional fee parameter
to V2 substreams so factories with non-standard fees emit the correct
value. Update PepeDex and Linkswap to 25 bps, fork e1046f to 20 bps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assert fee <= 10000 bps after deserialization to catch misconfigured
YAML params early instead of silently propagating invalid fee values
to the simulation layer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Haikane
Copy link
Copy Markdown
Contributor Author

Haikane commented Feb 21, 2026

Review fixes pushed

Fee bounds check (c8e1f97a)

Added assert!(params.fee <= 10000) in map_pools_created after Params deserialization. Catches misconfigured YAML params (e.g., fee=300 thinking percent instead of bps) at substream startup instead of silently propagating invalid fee values.

Fee mismatch "blockers" — resolved (no YAML changes needed)

The review flagged contradictions between the PR description's spot-check table and the YAML configs. After on-chain verification of the actual swap contracts:

Factory YAML Actual On-Chain Verdict
PepeDex (0x460b) fee=25 25 bps (modified formula: 25/10000) YAML correct, PR description wrong
Linkswap (0x6967) fee=25 25 bps for LINK pairs, 30 bps for non-LINK YAML correct for LINK pairs*
Yape (0x46ad) default 30 30 bps (standard V2, unchanged) Default correct, PR description wrong
Fork 880ae0 default 30 30 bps (standard V2, RigelSwap) Default correct, PR description wrong

*Linkswap uses per-pool variable fees (25 bps for LINK pairs, 30 bps for non-LINK). The current architecture uses one fee per factory, so 25 bps is an approximation.

Fork 7386f6 — intentionally included

PR description says "no pools created yet" — confirmed the YAML is a placeholder for future indexing. No action needed.

Addresses review feedback: 44 individual extractors for low-liquidity
forks is a poor indexing_load/coverage ratio. Instead, merge all V2
forks into one extractor (ethereum-univ2-forks.yaml) and all V3 forks
into one (ethereum-univ3-forks.yaml).

Changes:
- V2 map_pools_created now parses comma-separated factory addresses
  with optional :fee suffix for per-factory fee overrides
- V3 map_pools_created now parses comma-separated factory addresses
- Delete 39 individual V2 fork YAMLs + 3 V3 fork YAMLs
- Create ethereum-univ2-forks.yaml (39 factories, 1 extractor)
- Create ethereum-univ3-forks.yaml (3 factories, 1 extractor)
- Update all integration tests to reference merged YAMLs
- Backward compatible: single-address params still work as before

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Haikane Haikane changed the title Add 46 UniV2/V3 fork factory substreams configs Add 42 UniV2/V3 fork factories as 2 merged extractors Feb 26, 2026
@Haikane
Copy link
Copy Markdown
Contributor Author

Haikane commented Feb 26, 2026

Merged 42 extractors into 2

Addressed the feedback about indexing_load/coverage ratio. Instead of 44 separate extractors (one per fork factory), all forks now share a single extractor per protocol version:

  • ethereum-univ2-forks.yaml — 39 V2 factory addresses, 1 extractor
  • ethereum-univ3-forks.yaml — 3 V3 factory addresses, 1 extractor

How it works

The map_pools_created module now accepts comma-separated factory addresses in the params string. filter_by_address already takes a Vec<Address>, so the downstream modules (store_pools, map_pool_events) work without changes — they track pools by pool address, not factory address.

For V2, three factories have non-standard fees (PepeDex 25bps, Linkswap 25bps, fork e1046f 20bps). These are encoded inline with a :fee suffix:

factory_address=addr1,addr2,696708db...:25,460b2005...:25,e1046fcb...:20&protocol_type_name=uniswap_v2_pool

At event time, the module looks up the emitting factory via _log.address and resolves the correct fee. Addresses without a suffix use the default 30 bps.

For V3, no fee handling is needed since fees come from the PoolCreated event itself. Params are just comma-separated addresses.

Backward compatibility

Single-address params (used by existing extractors like uniswap-v2, sushiswap, pancakeswap) still work exactly as before — a single address is just a one-element comma-split.

What changed

  • Deleted 39 individual V2 fork YAMLs + 3 V3 fork YAMLs
  • Created 2 merged YAMLs (one per protocol version)
  • Updated map_pools_created in both V2 and V3 to parse multi-factory params
  • Updated all integration test configs to reference the merged YAMLs
  • WASM builds, unit tests, and protocol-testing all pass

Integration test results — 42/42 pass

PASS: ethereum-btswap-v2
PASS: ethereum-crodefi-swap-v2
PASS: ethereum-hiswap-v2
PASS: ethereum-jmswap-v2
PASS: ethereum-kwikswap-v2
PASS: ethereum-linkswap-v2
PASS: ethereum-mintyswap-v2
PASS: ethereum-nineinch-v2
PASS: ethereum-oboswap-v2
PASS: ethereum-orionpool-v2
PASS: ethereum-pepedex-v2
PASS: ethereum-saitaswap-v2
PASS: ethereum-sakeswap-v2
PASS: ethereum-sumswap-v2
PASS: ethereum-swapfactory-4eef57-v2
PASS: ethereum-swaps-v2
PASS: ethereum-univ2-fork-0388c1-v2
PASS: ethereum-univ2-fork-115934-v2
PASS: ethereum-univ2-fork-1e2c21-v2
PASS: ethereum-univ2-fork-229457-v2
PASS: ethereum-univ2-fork-2f3db9-v2
PASS: ethereum-univ2-fork-3823ac-v2
PASS: ethereum-univ2-fork-41060a-v2
PASS: ethereum-univ2-fork-5bddc6-v2
PASS: ethereum-univ2-fork-5d8d3b-v2
PASS: ethereum-univ2-fork-644b39-v2
PASS: ethereum-univ2-fork-6c3d18-v2
PASS: ethereum-univ2-fork-7dda55-v2
PASS: ethereum-univ2-fork-880ae0-v2
PASS: ethereum-univ2-fork-8a93b6-v2
PASS: ethereum-univ2-fork-8f9f4c-v2
PASS: ethereum-univ2-fork-a206f6-v2
PASS: ethereum-univ2-fork-a40ec8-v2
PASS: ethereum-univ2-fork-bc67c8-v2
PASS: ethereum-univ2-fork-e1046f-v2
PASS: ethereum-univ2-fork-e81357-v2
PASS: ethereum-yape-v2
PASS: ethereum-youswap-v2
PASS: ethereum-shibaswap-v3
PASS: ethereum-mintyswap-v3
PASS: ethereum-blueprint-v3
PASS: ethereum-univ2-fork-7386f6-v2

=== RESULTS: 42 passed, 0 failed ===

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