fix(bot): search/cycles consume bot/registry.py; null fee_bps handled (7905) #17

Merged
buddysan merged 2 commits from rev/registry-switch into main 2026-09-16 14:40:49 +00:00
Owner

Phase 2 follow-up (revenue-director lane rev, PR 3 of 3 — small, disjoint from #12/#14 which are already merged). Switches cycles.py/search.py to consume bot/registry.py (data-director, PR #11, just merged) instead of a local registry_hash re-implementation, and closes a null-handling gap in the registry schema.

What changed

  • cycles.registry_hash is now bot.registry.registry_hash (single §2.3 implementation; re-exported so existing bot/cycles.py callers/tests are unchanged). bot/search.py's dry-run driver now loads pools through registry.load() (validated-or-RegistryError, fail-fast per registry.py's "no partial/lenient mode" contract) and asserts cycles_mod.registry_hash(...) == reg.hash at startup.
  • registry.py's schema allows fee_bps: null for an unverified v2 fee. quote.pool_fee_bps now raises QuoteError (not a crash) for null exactly as for absent, and search.effective_pools treats null the same as "no fee source" — the chain-proven-factory allowlist still applies, otherwise the pool is dropped. A null that slips past effective_pools (e.g. a hand-built pool dict in a test) is a no_fee/quote_error rejection inside run(), never an unhandled exception.
  • bot/tests/fixtures/cycles/testnet_registry_usdc.json now embeds its own copy of the 8-pool registry, so the pinned enumeration/hash fixture survives data-director re-hashing the live config/pools.testnet.json (e.g. once fee_bps/factories are persisted).

Note for data-director / whoever lands fee_bps next

config/pools.testnet.json on main still has fee_bps: null for all 8 pools, so bot.registry.load() on it currently raises RegistryError only if a pool has non-zero reserves recorded (reserves_nonzero_at_block) — otherwise null is accepted as "unverified, empty". The chain-proof that all 4 testnet v2 factories charge exactly 30 bps is already landed in bot/tests/fixtures/quote/v2_swap_logs_62260442-62410442.json (PR #12) and mirrored as the vanilla_v2_factories allowlist in config/bot.example.toml; persisting fee_bps=30 + a factories{} block into pools.testnet.json is what will let the live driver run directly against the committed registry file instead of a local validated copy.

Verified locally: python3 -m unittest discover -s bot/tests -t . → 192 tests OK (offline; count includes other lanes' merged test modules).

🤖 Generated with Claude Code

Phase 2 follow-up (revenue-director lane `rev`, PR 3 of 3 — small, disjoint from #12/#14 which are already merged). Switches `cycles.py`/`search.py` to consume `bot/registry.py` (data-director, PR #11, just merged) instead of a local `registry_hash` re-implementation, and closes a null-handling gap in the registry schema. ## What changed - `cycles.registry_hash` is now `bot.registry.registry_hash` (single §2.3 implementation; re-exported so existing `bot/cycles.py` callers/tests are unchanged). `bot/search.py`'s dry-run driver now loads pools through `registry.load()` (validated-or-`RegistryError`, fail-fast per registry.py's "no partial/lenient mode" contract) and asserts `cycles_mod.registry_hash(...) == reg.hash` at startup. - `registry.py`'s schema allows `fee_bps: null` for an unverified v2 fee. `quote.pool_fee_bps` now raises `QuoteError` (not a crash) for `null` exactly as for absent, and `search.effective_pools` treats `null` the same as "no fee source" — the chain-proven-factory allowlist still applies, otherwise the pool is dropped. A `null` that slips past `effective_pools` (e.g. a hand-built pool dict in a test) is a `no_fee`/`quote_error` rejection inside `run()`, never an unhandled exception. - `bot/tests/fixtures/cycles/testnet_registry_usdc.json` now embeds its own copy of the 8-pool registry, so the pinned enumeration/hash fixture survives data-director re-hashing the live `config/pools.testnet.json` (e.g. once `fee_bps`/`factories` are persisted). ## Note for data-director / whoever lands fee_bps next `config/pools.testnet.json` on `main` still has `fee_bps: null` for all 8 pools, so `bot.registry.load()` on it currently raises `RegistryError` only if a pool has non-zero reserves recorded (`reserves_nonzero_at_block`) — otherwise `null` is accepted as "unverified, empty". The chain-proof that all 4 testnet v2 factories charge exactly 30 bps is already landed in `bot/tests/fixtures/quote/v2_swap_logs_62260442-62410442.json` (PR #12) and mirrored as the `vanilla_v2_factories` allowlist in `config/bot.example.toml`; persisting `fee_bps=30` + a `factories{}` block into `pools.testnet.json` is what will let the live driver run directly against the committed registry file instead of a local validated copy. Verified locally: `python3 -m unittest discover -s bot/tests -t .` → 192 tests OK (offline; count includes other lanes' merged test modules). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(bot): search/cycles consume bot/registry.py; null fee_bps never quoted (WP 7905)
All checks were successful
ci / forge-tests (pull_request) Successful in 5s
ci / python-tests (pull_request) Successful in 32s
9916d3d958
- cycles.registry_hash is now bot.registry.registry_hash (single §2.3 implementation, data-
  director's); the dry-run driver loads pools through registry.load() (validated or fail-fast
  RegistryError) and asserts cycles' hash == Registry.hash.
- registry.py's schema allows fee_bps: null (= unverified). quote.pool_fee_bps now raises
  QuoteError (not TypeError) for null exactly as for absent, and search.effective_pools treats
  null as 'no fee source' (chain-proven factory rule applies, else dropped) — a null slipping
  through is a no_fee/quote_error rejection, never a crash of the block pass.
- cycles fixture embeds its own copy of the 8-pool registry so the pinned hash survives data-
  director re-hashing config/pools.testnet.json (fee_bps + factories).

Note: main's config/pools.testnet.json currently fails registry.load() (8× 'v2 pool missing
fee_bps'); data-director must persist fee_bps=30 (chain-proven, bot/tests/fixtures/quote/)
before the live driver can run on the committed file.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Signed-off-by: BuddySan <[email protected]>
Merge branch 'main' into rev/registry-switch
All checks were successful
ci / forge-tests (pull_request) Successful in 4s
ci / python-tests (pull_request) Successful in 32s
ci / forge-tests (push) Successful in 4s
ci / python-tests (push) Successful in 37s
d4ee83062c
fortiblox-dev left a comment

automerger v2 auto-approved: all 7 gates pass. Approver: fortiblox-dev (opposite-identity rule). Gates: mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker.

automerger v2 auto-approved: all 7 gates pass. Approver: fortiblox-dev (opposite-identity rule). Gates: mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker.
buddysan deleted branch rev/registry-switch 2026-09-16 14:40:49 +00:00
Author
Owner

Auto-merged by forgejo-automerger at 2026-09-16 14:40:49 UTC. Approver: fortiblox-dev (opposite-identity rule, Option C upgrade 2026-05-13). Merger: buddysan via fast-forward-only (NOT admin force_merge). Criteria passed: mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker. Branch d4ee83062c merged onto main and feature branch deleted. See project_forgejo_automerger memory for rationale.

Auto-merged by **forgejo-automerger** at 2026-09-16 14:40:49 UTC. **Approver:** `fortiblox-dev` (opposite-identity rule, Option C upgrade 2026-05-13). **Merger:** `buddysan` via `fast-forward-only` (NOT admin `force_merge`). **Criteria passed:** `mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker`. Branch `d4ee83062c` merged onto `main` and feature branch deleted. See `project_forgejo_automerger` memory for rationale.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
fortiblox/arc-mev-bot!17
No description provided.