perf(cold-start): verify-only fast path for Curve + Balancer (+ optional Curve code seed) - #31
Merged
Merged
Conversation
…onal code seed CurveColdStartPlanner now skips discovery when CurveMetadata.discovered_slots is already known (a prior discovery, a trace, or a registry): no pool-account/bytecode fetch and no cold-cache get_dy slot-faulting, just a single verify round over the known slots. This makes a known-read-set single-pool cold_start as cheap as the bundled cold_start_many storage-program path Uniswap V2/V3 use. The discover->verify path is unchanged when discovered_slots is empty. Adds optional CurveMetadata.code_seed (+ with_code_seed builder + CurveAdapter::code_seeds): a caller-supplied Vyper runtime, verified once against on-chain EXTCODEHASH (mismatch -> purged -> lazy fetch), removing the one lazy code fetch a Curve pool otherwise pays on its first simulate_swap. Additive, non-breaking. Tests: verify-only skips-discovery + unfetchable-slot repair (cold_start_adoption), code_seeds unit test (curve), positive Curve one-shot classification (bootstrap_many). Adds examples/curve_cold_start_phases.rs and updates docs (curve-adapter, README, benchmarks). 216 tests pass; clippy/doc/all-features clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ports the Curve verify-only fast path to BalancerV2ColdStartPlanner: when BalancerV2Metadata.balance_slots is already known (a prior discovery or a trace), skip the getPoolTokens discovery -- no vault-account fetch, no cold-cache faulting -- and warm exactly those slots in a single verify round, matching the bundled cold_start_many storage-program path. Config-supplied tokens are preserved (no getPoolTokens decode repopulates them). The discover->verify path is unchanged when balance_slots is empty. Adds balancer_cold_start_verify_only_skips_discovery. 217 tests pass; clippy/all-features/doc clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on
hardening/tier-2-polish(clean 2-commit diff).Problem
Curve and Balancer cold-start were far slower than Uniswap V2/V3 on a cold boot. Both use a discover→verify planner, but it always re-ran discovery even when the pool's read-set was already known — fetching the pool/vault code and faulting each
get_dy/getPoolTokensSLOAD one-at-a-time over RPC, then a second verify round. V2/V3 hydrate their whole hot state in one bundledeth_calland are fully offline immediately.Changes
Verify-only fast path (the core win). When a pool's read-set is already known —
CurveMetadata.discovered_slots/BalancerV2Metadata.balance_slots, populated by the first discovery, a trace, or a registry — the planner skips discovery entirely and warms exactly those slots in a single verify round, the same one-shot path V2/V3 use. Byte-for-byte unchanged when the read-set is empty (zero regression on first boot).CurveColdStartPlanner: verify-only whendiscovered_slotsis known.BalancerV2ColdStartPlanner: verify-only whenbalance_slotsis known (preserves the config-suppliedtokens, since there is nogetPoolTokensdecode to repopulate them).cold_start_many-eligible (supports_one_shot_hydration) once their read-set is known — folding into the same bundled bootstrap as V2/V3.Optional Curve bytecode seed.
CurveMetadata::with_code_seed(runtime): a caller-supplied Vyper runtime, verified once against on-chainEXTCODEHASH(mismatch → purged → falls back to lazy fetch, never a correctness risk), removing the one lazy code fetch a Curve pool otherwise pays on its first quote. Opt-in; makes sense for single-contract quotes (Curveget_dy).Tooling & docs.
examples/curve_cold_start_phases.rstimes discovery vs verify-only vscold_start_many; README /docs/curve-adapter.md/docs/benchmarks.mdupdated.Measured
Tricrypto2 (the showcase pool), fresh run: cold-start ~712 ms → ~110 ms (~6× faster). Corroborated by the controlled numbers in
docs/benchmarks.md— Curve 3pool 361→75 ms (4.8×), Balancer BAL/WETH 333→76 ms (4.4×). Warm quote latency (~80 µs) is unchanged; this is a cold-start optimization only.API surface
Additive only: a
CurveMetadata.code_seed: Option<Bytes>field +with_code_seedbuilder (#[non_exhaustive]struct → non-breaking). The verify-only speedup needs no new API — it rides on the existingcold_start/cold_start_manymethods reading metadata the crate populates itself, so default users get it automatically on every warm after the first.Verification
217 tests pass; clippy (all-targets/all-features),
cargo doc(all-features,#![warn(missing_docs)]), and the all-features build are clean. New tests: verify-only skip-discovery + unfetchable-slot repair (Curve + Balancer), thecode_seedshook, and positive one-shot classification.Follow-up
First-ever boot of an unknown pool still pays one-time discovery. A generalized
eth_createAccessList-based discovery (fetch the access list from the node in one call, then one-shot batch-load those slots — cold warming in two shots) would make first boot fast by default with no new user API. Planned as a separate PR.🤖 Generated with Claude Code