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¶
Merge the five math crates into one new crate
degenbot-mathwith modulesv2,cl,curve,balancer,solidly. Paths read asdegenbot_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 threeoracle_crosschecksuites + 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.Umbrella alias convention — whole-crate re-exports strip the
degenbot_prefix (precedent:alloydoespub use alloy_sol_types as sol_types;, and alloy’s ownpub 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(thedegenbot-solverscrate takes the bare name), anddegenbot::cmd_executor— an intentional non-strip: bareexecutorwould be a one-letter visual collision withdegenbot::execution, so the command-executor keeps its disambiguatingcmd_.Partial (module-level) hoists stay, plus the whole-crate alias alongside:
degenbot::core(alias) withaddress_utils/errors/hex_utils/runtime/eip_1559still hoisted to the root;degenbot::bot(alias) withbot_corestill hoisted — thesolvershoist fromdegenbot_botis dropped sodegenbot::solversnames the solvers crate; the engine lives atdegenbot::bot::solvers::arb_engine[amended: hoisted todegenbot_bot::arb_engine/degenbot::bot::arb_engineby the module-organization cut].degenbot::uniswap(alias) withdex_identity/v2_encodingstill hoisted. Root convenience hoists of items (BotState, PoolEntry, builders, lifecycles,preset_for_variant,DexIdentity,ReservesAbi, …) are unchanged.degenbot-solverskeeps its identity — it is path-level optimization math that consumes the math crates; merging it intodegenbot-mathwould invert a dependency.degenbot-forkkeeps 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_<x>_math→degenbot_math::<mod>).Umbrella in-crate paths (
examples/standalone_consumer.rs, its tests) move to the aliased paths.Python layer:
degenbot-python(thedegenbot_rswheel crate) imports the math crates atdegenbot_math::<mod>::paths — the wheel’s public API is unchanged.just test-tier3 stepfamily retargetspkg=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.