@@ -34,10 +34,12 @@ use ::degenbot_pools::registry::PoolEntry;
3434use :: degenbot_pools:: simulate_swap:: simulate_swap;
3535use :: degenbot_pools:: tick_fetch:: { FetchedTickWord , TickWordFetcher } ;
3636use :: degenbot_pools:: v3_state:: {
37- v3_simulate_swap, PoolTickCoverage , RegisterV3PoolParams , SimulateSwapError , V3PoolState ,
38- V3SwapOutcome ,
37+ v3_simulate_swap, PoolTickCoverage , RegisterV3PoolParams , SimulateSwapError , V3PoolIdentity ,
38+ V3PoolState , V3SwapOutcome ,
39+ } ;
40+ use :: degenbot_pools:: v4_state:: {
41+ v4_simulate_swap, RegisterV4PoolParams , V4PoolIdentity , V4PoolState ,
3942} ;
40- use :: degenbot_pools:: v4_state:: { v4_simulate_swap, RegisterV4PoolParams , V4PoolState } ;
4143
4244use super :: BotState ;
4345
@@ -394,7 +396,7 @@ struct OverrideSim<'a> {
394396 fetcher : Option < Arc < dyn TickWordFetcher > > ,
395397 pool_id : u64 ,
396398 /// RATR5A/CXRHW3: miss recovery DISARMED - a fresh missing word
397- /// surfaces as FetchExhausted (typed contract) instead of an inline
399+ /// surfaces as ` FetchExhausted` (typed contract) instead of an inline
398400 /// fetch under the caller read guard.
399401 disarm_fetch : bool ,
400402}
@@ -583,33 +585,7 @@ impl BotState {
583585 | PoolEntry :: AerodromeV2 ( ..) => Some ( Vec :: new ( ) ) ,
584586 PoolEntry :: V3 ( identity, st) => {
585587 let mut missing = Vec :: new ( ) ;
586- let mut transient = TransientCl {
587- family : TransientFamily :: V3 ( identity. fee , identity. tick_spacing ) ,
588- inner : TransientInner :: V3 ( Box :: new (
589- V3PoolState :: from_params (
590- RegisterV3PoolParams {
591- address : identity. address ,
592- token0 : identity. token0 ,
593- token1 : identity. token1 ,
594- fee : identity. fee ,
595- tick_spacing : identity. tick_spacing ,
596- factory : identity. factory ,
597- deployer : identity. deployer ,
598- init_hash : identity. init_hash ,
599- sqrt_price_x96 : st. sqrt_price_x96 ,
600- liquidity : st. liquidity ,
601- tick : st. tick ,
602- tick_data : st. tick_data . clone ( ) ,
603- update_block : st. update_block ,
604- coverage : PoolTickCoverage :: Sparse ,
605- fetcher : None ,
606- ..Default :: default ( )
607- } ,
608- self . journal_depth ,
609- )
610- . 1 ,
611- ) ) ,
612- } ;
588+ let mut transient = self . v3_discovery_transient ( identity, st) ;
613589 let spec =
614590 engine_amount_specified ( request. amount_specified , EngineFamily :: V3Engine ) ;
615591 let limit = request
@@ -626,33 +602,7 @@ impl BotState {
626602 }
627603 PoolEntry :: V4 ( identity, st) => {
628604 let mut missing = Vec :: new ( ) ;
629- let mut transient = TransientCl {
630- family : TransientFamily :: V4 (
631- identity. pool_key . fee ,
632- identity. pool_key . tick_spacing ,
633- ) ,
634- inner : TransientInner :: V4 ( Box :: new (
635- V4PoolState :: from_params (
636- RegisterV4PoolParams {
637- pool_manager : identity. pool_manager ,
638- pool_id : identity. pool_id ,
639- pool_key : identity. pool_key . clone ( ) ,
640- hook_flags : 0 ,
641- protocol_fee : 0 ,
642- sqrt_price_x96 : st. sqrt_price_x96 ,
643- liquidity : st. liquidity ,
644- tick : st. tick ,
645- tick_data : st. tick_data . clone ( ) ,
646- update_block : st. update_block ,
647- tick_data_block : None ,
648- coverage : PoolTickCoverage :: Sparse ,
649- fetcher : None ,
650- } ,
651- self . journal_depth ,
652- )
653- . 1 ,
654- ) ) ,
655- } ;
605+ let mut transient = self . v4_discovery_transient ( identity, st) ;
656606 let spec =
657607 engine_amount_specified ( request. amount_specified , EngineFamily :: V4Engine ) ;
658608 let limit = request
@@ -671,6 +621,75 @@ impl BotState {
671621 }
672622 }
673623
624+ /// Build the V3 discovery transient for a registered pool: it mirrors
625+ /// the REGISTERED pool's coverage (a Tracked pool never raises
626+ /// `MissingTickWord`, so the staging walk must not invent fetch work
627+ /// for one) AND its checked-word set (T1 3WTDFK: `from_params` seeds
628+ /// known words from tick ROWS only — a caller-CHECKED empty word has no
629+ /// rows yet must never become a fetch target).
630+ fn v3_discovery_transient ( & self , identity : & V3PoolIdentity , st : & V3PoolState ) -> TransientCl {
631+ let mut v3_transient = V3PoolState :: from_params (
632+ RegisterV3PoolParams {
633+ address : identity. address ,
634+ token0 : identity. token0 ,
635+ token1 : identity. token1 ,
636+ fee : identity. fee ,
637+ tick_spacing : identity. tick_spacing ,
638+ factory : identity. factory ,
639+ deployer : identity. deployer ,
640+ init_hash : identity. init_hash ,
641+ sqrt_price_x96 : st. sqrt_price_x96 ,
642+ liquidity : st. liquidity ,
643+ tick : st. tick ,
644+ tick_data : st. tick_data . clone ( ) ,
645+ update_block : st. update_block ,
646+ coverage : st. coverage ,
647+ fetcher : None ,
648+ ..Default :: default ( )
649+ } ,
650+ self . journal_depth ,
651+ )
652+ . 1 ;
653+ v3_transient
654+ . known_bitmap_words
655+ . extend ( st. known_bitmap_words . iter ( ) . copied ( ) ) ;
656+ TransientCl {
657+ family : TransientFamily :: V3 ( identity. fee , identity. tick_spacing ) ,
658+ inner : TransientInner :: V3 ( Box :: new ( v3_transient) ) ,
659+ }
660+ }
661+
662+ /// V4 twin of [`Self::v3_discovery_transient`] — the same coverage +
663+ /// checked-word mirroring discipline (see that doc).
664+ fn v4_discovery_transient ( & self , identity : & V4PoolIdentity , st : & V4PoolState ) -> TransientCl {
665+ let mut v4_transient = V4PoolState :: from_params (
666+ RegisterV4PoolParams {
667+ pool_manager : identity. pool_manager ,
668+ pool_id : identity. pool_id ,
669+ pool_key : identity. pool_key . clone ( ) ,
670+ hook_flags : 0 ,
671+ protocol_fee : 0 ,
672+ sqrt_price_x96 : st. sqrt_price_x96 ,
673+ liquidity : st. liquidity ,
674+ tick : st. tick ,
675+ tick_data : st. tick_data . clone ( ) ,
676+ update_block : st. update_block ,
677+ tick_data_block : None ,
678+ coverage : st. coverage ,
679+ fetcher : None ,
680+ } ,
681+ self . journal_depth ,
682+ )
683+ . 1 ;
684+ v4_transient
685+ . known_bitmap_words
686+ . extend ( st. known_bitmap_words . iter ( ) . copied ( ) ) ;
687+ TransientCl {
688+ family : TransientFamily :: V4 ( identity. pool_key . fee , identity. pool_key . tick_spacing ) ,
689+ inner : TransientInner :: V4 ( Box :: new ( v4_transient) ) ,
690+ }
691+ }
692+
674693 /// The stored word fetcher for a registered pool, if any — the staged
675694 /// pre-pass fetches through it OUTSIDE any state lock.
676695 #[ must_use]
@@ -731,7 +750,7 @@ impl BotState {
731750 /// RATR5A/CXRHW3: the override sim with miss recovery DISARMED - a
732751 /// missing word surfaces as None (the legacy Option contract) instead of
733752 /// an inline fetch under the caller read guard. The pooled caller
734- /// pre-stages through [Self::override_missing_words] + the lock-free
753+ /// pre-stages through [` Self::override_missing_words` ] + the lock-free
735754 /// fetch choreography before entering.
736755 pub fn simulate_override_disarmed (
737756 & self ,
@@ -745,7 +764,7 @@ impl BotState {
745764 /// listed by a collect-only walk over a transient built from the
746765 /// override scalars + the caller tick data (no fetch, no registered
747766 /// mutation). Pair with the lock-free fetch choreography in pool.rs
748- /// (ensure_override_missing_staged).
767+ /// (` ensure_override_missing_staged` ).
749768 #[ must_use]
750769 pub fn override_missing_words ( & self , over : & OverrideSwap ) -> Option < Vec < i32 > > {
751770 if over. request . amount_specified . is_zero ( ) {
@@ -843,6 +862,7 @@ impl BotState {
843862 }
844863 }
845864
865+ #[ must_use]
846866 pub fn simulate_override ( & self , over : & OverrideSwap , block : u64 ) -> Option < V3SwapOutcome > {
847867 self . simulate_override_ext ( over, block, false )
848868 }
@@ -960,17 +980,13 @@ impl BotState {
960980 ///
961981 /// `block` is the fetch context threaded into the tick-word fetcher on
962982 /// sparse-miss recovery; it does not affect pure computation.
963- // TODO(X4EU3J follow-up): extract the per-family arms once the hook
964- // caveat plumbing settles; the body is 101 lines against a 100-line
965- // clippy::too_many_lines budget.
966- #[ expect( clippy:: too_many_lines) ]
967983 pub fn swap_simulation ( & mut self , block : u64 , pool_id : u64 , request : SwapRequest ) -> SwapRead {
968984 self . swap_simulation_ext ( block, pool_id, & request, false )
969985 }
970986
971987 /// RATR5A/CXRHW3: the swap with miss recovery DISARMED — a residual
972988 /// missing word after the staged pre-pass surfaces as the typed
973- /// FetchExhausted contract (additive, ADR-037) instead of fetching under
989+ /// ` FetchExhausted` contract (additive, ADR-037) instead of fetching under
974990 /// the caller write guard. Identical arithmetic otherwise (the caller
975991 /// must have staged the missing words via the lock-free pre-pass).
976992 pub fn swap_simulation_disarmed (
@@ -982,6 +998,10 @@ impl BotState {
982998 self . swap_simulation_ext ( block, pool_id, request, true )
983999 }
9841000
1001+ // TODO(X4EU3J follow-up): extract the per-family arms once the hook
1002+ // caveat plumbing settles; the body is 114 lines against a 100-line
1003+ // clippy::too_many_lines budget.
1004+ #[ expect( clippy:: too_many_lines) ]
9851005 fn swap_simulation_ext (
9861006 & mut self ,
9871007 block : u64 ,
@@ -1078,7 +1098,7 @@ impl BotState {
10781098 block,
10791099 SwapOutcomeFamily :: V3 ,
10801100 coverage,
1081- request. clone ( ) ,
1101+ * request,
10821102 Caveats :: default ( ) ,
10831103 )
10841104 }
@@ -1108,7 +1128,7 @@ impl BotState {
11081128 block,
11091129 SwapOutcomeFamily :: V4 ,
11101130 coverage,
1111- request. clone ( ) ,
1131+ * request,
11121132 extra_caveats,
11131133 )
11141134 }
@@ -1148,7 +1168,12 @@ fn finish_cl(
11481168
11491169#[ cfg( test) ]
11501170mod tests {
1151- #![ expect( clippy:: panic, clippy:: unwrap_used, clippy:: used_underscore_binding) ]
1171+ #![ expect(
1172+ clippy:: panic,
1173+ clippy:: unwrap_used,
1174+ clippy:: expect_used,
1175+ clippy:: used_underscore_binding
1176+ ) ]
11521177
11531178 use super :: * ;
11541179 use hashbrown:: HashMap ;
@@ -1160,7 +1185,6 @@ mod tests {
11601185
11611186 /// Registered sparse pool whose stored fetcher counts every call.
11621187 fn counting_fetcher_setup ( calls : Arc < AtomicUsize > ) -> ( BotState , u64 ) {
1163- let mut core = BotState :: new ( ) ;
11641188 #[ derive( Debug ) ]
11651189 struct CountingFetcher ( Arc < AtomicUsize > ) ;
11661190 impl :: degenbot_pools:: tick_fetch:: TickWordFetcher for CountingFetcher {
@@ -1180,6 +1204,7 @@ mod tests {
11801204 } )
11811205 }
11821206 }
1207+ let mut core = BotState :: new ( ) ;
11831208 let pool_id = core
11841209 . register_v3_pool ( & RegisterV3PoolParams {
11851210 address : alloy:: primitives:: Address :: ZERO ,
0 commit comments