# ADR-035: Consolidate the AMM math family into `degenbot-math` + umbrella alias convention **Status:** Accepted **Date:** 2026-08-20 **Task:** ergo ZUOZDR ## Context - The workspace had a set of publishable crates; five of them were AMM invariant-math ports: `degenbot-v2-math`, `degenbot-concentrated-liquidity-math`, `degenbot-curve-math`, `degenbot-balancer-math`, `degenbot-solidly-math`. - crates.io has **no name reservation** — every published name is a permanent footprint: its own README, keywords, dependents, and version lineage. - The research handoff (`docs/handoffs/crates-io-publishing-prep.md`) warns against over-splitting (citing ruff/uv internals as a caution) and its "don't shrink" guidance assumed a *post-publish* world. Pre-first-publish, consolidation is free. - Crate identity test for publishing: (1) does it have value types / API a consumer uses directly? (2) independent external-reuse demand? (3) version independence? (4) a natural home for docs + keywords? Facts about the five math crates: all carry the ADR-009 lockstep version (one version literal in the tree — zero independence); the same consumer set (pools, bot, solvers, umbrella; db + rpc for CL); no consumer wants a single family in isolation; no external-reuse demand signal; each is byte-exact ports of canonical Solidity math (canonical sources cited in each module doc). ## Decision 1. **Merge the five math crates into one new crate `degenbot-math`** with modules `v2`, `cl`, `curve`, `balancer`, `solidly`. Paths read as `degenbot_math::cl::`, `degenbot_math::curve::CurveDyCalculator`, etc. (each family's current root re-exports become its module root). Test suites move intact: the CL tier-3 REVM oracle (`tier3_compute_swap_step_vs_revm` + proptest regressions), the three `oracle_crosscheck` suites + snapshots (renamed with family suffixes to avoid name collisions), and the threeCriterion benches. The consolidation reduced both the workspace-member and publishable-crate counts by four. The five old names are never published — no deprecation path needed. 2. **Umbrella alias convention** — whole-crate re-exports strip the `degenbot_` prefix (precedent: `alloy` does `pub use alloy_sol_types as sol_types;`, and alloy's own `pub use alloy_primitives as primitives;` shows even a *name clash inside the umbrella* is handled by giving the sub-crate the shorter name): `degenbot::pools`, `degenbot::rpc`, `degenbot::db`, `degenbot::fork`, `degenbot::simulation`, `degenbot::arbitrage`, `degenbot::submission`, `degenbot::execution`, `degenbot::decoders`, `degenbot::price`, `degenbot::aave`, `degenbot::pool_updater`, `degenbot::pathfinding`, `degenbot::order_index`, `degenbot::abi`, `degenbot::math`, `degenbot::solvers` (the `degenbot-solvers` crate takes the bare name), and `degenbot::cmd_executor` — an **intentional non-strip**: bare `executor` would be a one-letter visual collision with `degenbot::execution`, so the command-executor keeps its disambiguating `cmd_`. 3. **Partial (module-level) hoists stay**, plus the whole-crate alias alongside: `degenbot::core` (alias) with `address_utils`/`errors`/`hex_utils`/`runtime`/`eip_1559` still hoisted to the root; `degenbot::bot` (alias) with `bot_core` still hoisted — the `solvers` hoist from `degenbot_bot` is **dropped** so `degenbot::solvers` names the solvers *crate*; the engine lives at `degenbot::bot::solvers::arb_engine` **[amended: hoisted to `degenbot_bot::arb_engine` / `degenbot::bot::arb_engine` by the module-organization cut]**. `degenbot::uniswap` (alias) with `dex_identity`/`v2_encoding` still hoisted. Root convenience hoists of items (BotState, PoolEntry, builders, lifecycles, `preset_for_variant`, `DexIdentity`, `ReservesAbi`, …) are unchanged. 4. **`degenbot-solvers` keeps its identity** — it is path-level optimization math that *consumes* the math crates; merging it into `degenbot-math` would invert a dependency. **`degenbot-fork` keeps its identity**; the runtime-vs-dev split question is a separate follow-up (see MYYV2X scope note). ## Alternatives considered - **Status quo (the original crate identities):** the identity test fails for the math family (no independent consumers, no version independence, one provenance story); the footprint is a permanent cost. - **Merge the math into `degenbot-pools`:** pools is pool/token *state*; the math crates have independent consumers (solvers, db, rpc, simulation) that would then pull all of pools. Blurs two identities instead of clarifying one. - **Defer until post-publish:** impossible in practice — after first publish, splitting a crate back out is deprecation + redirects + version drift, the expensive direction. ## Impact / migration - 8 consumer manifests re-point (pools, bot, solvers, db, rpc, umbrella, python, simulation-dev) and ~59 source files get a scripted, longest-name-first import rewrite (`degenbot__math` → `degenbot_math::`). - Umbrella in-crate paths (`examples/standalone_consumer.rs`, its tests) move to the aliased paths. - Python layer: `degenbot-python` (the `degenbot_rs` wheel crate) imports the math crates at `degenbot_math::::` paths — the wheel's public API is unchanged. - `just test-tier3 step` family retargets `pkg=degenbot-math`. - MYYV2X (CI dry-run gauntlet) and S7R5K4 (publish loop) operate on the recomputed 22-crate order; S7R5K4's name-reprobe list is 24 names (22 + `degenbot_rs` + the example crate). - Escape hatch: extracting a family back into its own crate later is an ordinary refactor + `cargo new` — allowed when real versioning demand appears. ## Verification (gates) Existing oracle/unit suites run unchanged in the new crate home (the crosscheck + REVM oracles are the safety net); `just check-no-pyo3-in-cores` unchanged; G1 `just publish-dry-run` green on the final 22-crate layout; `cargo build --workspace` + full workspace test run green. One refactor commit, tagged with the ergo task.