Skip to content

feat: Opening Auction support for Base Sepolia#84

Open
z80dev wants to merge 6 commits intomainfrom
feat/opening-auction-base-sepolia
Open

feat: Opening Auction support for Base Sepolia#84
z80dev wants to merge 6 commits intomainfrom
feat/opening-auction-base-sepolia

Conversation

@z80dev
Copy link
Collaborator

@z80dev z80dev commented Feb 18, 2026

Summary

Adds full SDK support for the Opening Auction feature on Base Sepolia.

What's Included

Core Features

  • OpeningAuctionBuilder - Build params for creating opening auction launches
  • OpeningAuctionLifecycle - Settlement, completion, and recovery helpers
  • OpeningAuctionPositionManager - LP position management for auction bidding
  • OpeningAuction - Direct hook interaction helpers

Mining & Address Generation

  • Auto-mining for OpeningAuction hook addresses
  • Auto-mining for Doppler hook addresses (completion path)
  • Token address mining with proper ordering

Base Sepolia Deployment

  • OpeningAuctionInitializer: 0x3dCd35945Dc86a9FaA80846B06CB4676961d0AEa
  • OpeningAuctionPositionManager: 0x957CA7472ced1C1B3608152F83E0E69F975a37a9

Testing

  • 8 new unit test files
  • 1 new fork test for Base Sepolia
  • All 77 fork tests passing
  • All 399 unit tests passing

Examples

  • opening-auction-bidding.ts - Bid on opening auctions
  • opening-auction-lifecycle.ts - Full lifecycle management

Checklist

  • Contracts deployed and verified on Base Sepolia
  • Initializer whitelisted in Airlock
  • SDK addresses updated
  • Unit tests passing
  • Fork tests passing
  • Examples provided
  • Documentation updated

Related

  • doppler/contracts: Opening auction implementation
  • Deployment: Base Sepolia testnet

Adds full SDK support for Opening Auction lifecycle:
- OpeningAuctionBuilder for creating auction params
- OpeningAuctionLifecycle entity for settlement/completion
- OpeningAuctionPositionManager entity for LP management
- OpeningAuction entity for hook interaction
- Auto-mining for hook addresses and token addresses
- Completion mining for Doppler salt transition
- Base Sepolia deployment addresses
- Full test coverage (unit + fork tests)
- Examples for bidding and lifecycle
@z80dev
Copy link
Collaborator Author

z80dev commented Feb 24, 2026

Follow-up update from dual-agent review (Claude + Codex):\n\n- Normalized tuple bigint/number fields in opening-auction reads (, lifecycle ).\n- Added positive-liquidity guards across bid place/withdraw paths to prevent invalid/inverted ops.\n- Added/expanded opening-auction unit coverage (including new suite) and liquidity math unit tests.\n- Improved owner-indexed position enumeration end-of-list handling to reduce unnecessary global scan fallback.\n- Added fork bid-management test coverage and stricter async handling in opening-auction fork smoke tests.\n- Exported missing opening-auction public types from barrel/root exports.\n\nValidation run locally:\n-

@whetstone-research/doppler-sdk@0.0.17 typecheck /Users/z80/dev/doppler-sdk
tsc --noEmit\n-
RUN v2.1.9 /Users/z80/dev/doppler-sdk

stdout | test/unit/entities/OpeningAuctionBidManager.test.ts
[vitest.setup] Test mode: unit

stdout | test/unit/entities/DopplerSDK.openingAuctionPositionManager.test.ts
[vitest.setup] Test mode: unit

stdout | test/unit/entities/OpeningAuctionLifecycle.test.ts
[vitest.setup] Test mode: unit

stdout | test/unit/entities/DopplerSDK.openingAuctionLifecycle.test.ts
[vitest.setup] Test mode: unit

stdout | test/unit/entities/OpeningAuction.test.ts
[vitest.setup] Test mode: unit

stdout | test/unit/entities/OpeningAuctionPositionManager.test.ts
[vitest.setup] Test mode: unit

✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > getAddress > returns the initializer address
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > getState > normalizes tuple state and tuple pool key
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > getState > normalizes object state and object pool key numeric/boolean fields
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > hook getters > getOpeningAuctionHook reads initializer hook mapping
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > hook getters > getDopplerHook reads initializer hook mapping
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > position manager > reads the initializer position manager address
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > complete auction > simulateCompleteAuction uses wallet account by default
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > complete auction > simulateCompleteAuction accepts explicit account override
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > complete auction > completeAuction simulates then writes transaction
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > complete auction > completeAuction throws when wallet client is missing
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > recover opening auction incentives > simulateRecoverOpeningAuctionIncentives defaults account to wallet
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > recover opening auction incentives > simulateRecoverOpeningAuctionIncentives accepts explicit account
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > recover opening auction incentives > recoverOpeningAuctionIncentives writes the simulated request
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > recover opening auction incentives > recoverOpeningAuctionIncentives throws when wallet client is missing
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > sweep opening auction incentives > simulateSweepOpeningAuctionIncentives defaults account to wallet
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > sweep opening auction incentives > simulateSweepOpeningAuctionIncentives accepts explicit account
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > sweep opening auction incentives > sweepOpeningAuctionIncentives writes the simulated request
✓ test/unit/entities/OpeningAuctionLifecycle.test.ts > OpeningAuctionLifecycle > sweep opening auction incentives > sweepOpeningAuctionIncentives throws when wallet client is missing
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > getAddress > returns the position manager address
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > getPoolManager > reads the pool manager address via readContract
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > call encoding > encodeOwnerHookData defaults to packed 20-byte encoding
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > call encoding > encodeOwnerHookData supports explicit abi format opt-in
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > call encoding > encodeOwnerHookData supports packed 20-byte encoding
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > call encoding > decodeDelta splits BalanceDelta into two int128 values
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > call encoding > computePositionKey matches keccak256(abi.encodePacked(...)) shape
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > buildSingleTickParams > sets tickUpper using tickSpacing and defaults salt to zero
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > buildSingleTickParams > respects the provided salt instead of the default
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > modifyLiquidity > simulateModifyLiquidity uses wallet account by default
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > modifyLiquidity > simulateModifyLiquidity accepts explicit account override
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > modifyLiquidity > modifyLiquidity simulates then writes transaction
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > modifyLiquidity > modifyLiquidity throws when wallet client is missing
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > modifyLiquidityWithHookData > simulateModifyLiquidityWithHookData uses wallet account by default
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > modifyLiquidityWithHookData > simulateModifyLiquidityWithHookData accepts explicit account override
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > modifyLiquidityWithHookData > modifyLiquidityWithHookData simulates then writes transaction
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > modifyLiquidityWithHookData > modifyLiquidityWithHookData throws when wallet client is missing
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > simulatePlaceBid > builds single-tick params and forwards to simulateModifyLiquidity when no hookData
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > simulatePlaceBid > routes through simulateModifyLiquidityWithHookData when hookData is provided
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > simulatePlaceBid > throws when liquidity is non-positive
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > placeBid > uses modifyLiquidity when hookData is omitted
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > placeBid > calls modifyLiquidityWithHookData when hookData is present
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > placeBid > throws when liquidity is non-positive
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > simulateWithdrawBid > negates liquidityDelta when forwarding to simulateModifyLiquidity
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > simulateWithdrawBid > uses simulateModifyLiquidityWithHookData when hookData is provided
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > simulateWithdrawBid > throws when liquidity is non-positive
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > withdrawBid > calls modifyLiquidity with negative liquidityDelta when no hookData
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > withdrawBid > prefers modifyLiquidityWithHookData when hookData exists
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > withdrawBid > throws when liquidity is non-positive
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > simulateWithdrawFullBid > resolves positionId + liquidity then simulates a full withdrawal
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > simulateWithdrawFullBid > throws when the position is not found
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > withdrawFullBid > reads liquidity and executes a full withdrawal
✓ test/unit/entities/OpeningAuctionPositionManager.test.ts > OpeningAuctionPositionManager > withdrawFullBid > throws when wallet client is missing
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > validateBid > returns invalid with actionable errors for bad liquidity / settled phase
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > validateBid > returns warning when bid is above estimated clearing tick
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > validateBid > handles token1-side warning semantics (isToken0=false)
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > quoteFromTokenAmount > throws when tokenAmount is non-positive
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > quoteFromTokenAmount > throws when tokenIndex does not match auction side
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > quoteFromTokenAmount > throws when tokenIndex mismatches token1-side auction (isToken0=false)
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > simulateClaimIncentives (single position) > resolves positionId and returns simulation payload
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > fromHookAddress > constructs manager by reading poolKey + positionManager from hook
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getOwnerBids > returns empty array when owner has no positions
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getOwnerBids > resolves owner from walletClient when not provided
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getOwnerBids > enumerates positions and returns enriched bid info
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getOwnerBidStatuses > returns enriched statuses with clearing tick info
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getMultiplePositionInfos > returns empty array for empty input
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getMultiplePositionInfos > fetches and enriches multiple positions sorted by ID
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getTickLiquidityDistribution > reads liquidity across a tick range
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getTickLiquidityDistribution > defaults step to pool tickSpacing
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getTickLiquidityDistribution > throws for non-integer step
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getTickLiquidityDistribution > throws when startTick > endTick
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getTickLiquidityDistribution > throws when tick range exceeds 1000 ticks
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getTickLiquidityDistribution > throws when startTick is not aligned to tickSpacing
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getTickLiquidityDistribution > throws when endTick is not aligned to tickSpacing
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getTickLiquidityDistribution > throws when step is not a multiple of tickSpacing
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchBidPlaced > calls watchContractEvent with correct params and returns unsubscribe
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchBidPlaced > parses log events and calls onBidPlaced callback
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchBidWithdrawn > calls watchContractEvent with positionId filter
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchBidWithdrawn > parses log events and calls onBidWithdrawn callback
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchIncentivesClaimed > supports both owner and positionId filters
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchIncentivesClaimed > parses log events and calls onIncentivesClaimed callback
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchPhaseChange > sets up watcher for PhaseChanged events
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchPhaseChange > parses log events and calls onPhaseChanged callback
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchEstimatedClearingTick > sets up watcher for EstimatedClearingTickUpdated events
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchEstimatedClearingTick > parses log events and calls onEstimatedClearingTickUpdated callback
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getBidStatus (enriched) > returns enriched status with clearing tick fields for existing position
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > getBidStatus (enriched) > returns default status when position does not exist
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > increaseBid > delegates to simulatePlaceBid for simulation
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > increaseBid > delegates to estimatePlaceBidGas for gas estimation
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > increaseBid > delegates to placeBid for execution
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > decreaseBid > delegates to simulateWithdrawBid for simulation
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > decreaseBid > delegates to estimateWithdrawBidGas for gas estimation
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > decreaseBid > rejects simulateWithdrawBid when liquidity is non-positive
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > decreaseBid > throws when phase is Active (phase 1)
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > decreaseBid > succeeds when phase is not Active
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > moveBid > throws when fromTickLower === toTickLower
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > moveBid > throws when source position not found
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > moveBid > throws when source position has zero liquidity
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > moveBid > simulates both withdraw and place legs
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > moveBid > executes both transactions and returns both hashes
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > moveBid > throws when no wallet client for execution
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > moveBid > validates fromTickLower !== toTickLower in moveBid execution
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > quoteBid > returns a complete quote with simulation data and clearing info
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > quoteBid > handles getLiquidityAtTick failure gracefully
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > claimAllIncentives > previews claimable positions correctly
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > claimAllIncentives > claims all eligible positions sequentially
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > claimAllIncentives > stops on first error when continueOnError is false
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > claimAllIncentives > continues on error when continueOnError is true
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > claimAllIncentives > throws when no wallet client for claimAllIncentives
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchBidStatus > sets up watchBlockNumber and returns unsubscribe function
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getAddress > returns the hook address
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getPositionManager > reads the position manager address
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getPositionHarvestedTime > reads the harvested-time snapshot for a position
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getPosition > converts tuple responses from the contract
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getPosition > handles struct-like responses
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > isInRange > uses native hook isInRange when available
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > isInRange > computes client-side based on phase and isToken0
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > isInRange > returns false for NotStarted phase
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > calculateIncentives > calls the hook with the position id
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > settleAuction > simulates then writes to the wallet client
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > settleAuction > throws when no wallet client is configured
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > claimIncentives > simulates then writes the claim call
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > claimIncentives > throws when no wallet client is configured
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > position-id helpers > getPositionId computes key client-side then reads positionKeyToId
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > position-id helpers > getPositionIdFromKey reads the positionKeyToId mapping
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getEstimatedClearingTick > reads the estimated clearing tick and converts to number
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getLiquidityAtTick > reads liquidity for a valid tick
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getLiquidityAtTick > throws for non-integer tick
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getLiquidityAtTick > throws for tick below int24 min
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getLiquidityAtTick > throws for tick above int24 max
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getLiquidityAtTick > accepts tick at int24 boundary values
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getNextPositionId > reads the next position id
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getNextPositionId > throws when nextPositionId returns 0n (malformed state)
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getOwnerPositionIdAt > reads the owner position id at the given index
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getOwnerPositions > uses owner-indexed enumeration when available
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getOwnerPositions > returns empty array when nextPositionId is 1 (no positions)
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > getOwnerPositions > scans and filters positions by owner with non-zero liquidity
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > isInRange - comprehensive > returns false for zero-address owner (non-existent position)
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > isInRange - comprehensive > returns true for isToken0=false when refTick >= tickLower
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > isInRange - comprehensive > returns false for isToken0=false when refTick < tickLower
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > isInRange - comprehensive > returns false for isToken0=true when refTick >= tickUpper
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > isInRange - comprehensive > uses clearingTick for settled phase
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > simulateSettleAuction > returns request and gas estimate from simulation
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > simulateSettleAuction > falls back to estimateContractGas when request has no gas field
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > estimateSettleAuctionGas > returns the gas estimate from simulateSettleAuction
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > simulateClaimIncentives > returns request and gas estimate for a position
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > simulateClaimIncentives > falls back to estimateContractGas when request has no gas field
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > estimateClaimIncentivesGas > returns the gas estimate from simulateClaimIncentives
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > watchAuctionSettled > calls watchContractEvent with correct params and returns unsubscribe
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > watchAuctionSettled > parses log events and calls onSettled callback
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > watchAuctionSettled > handles missing args gracefully with defaults
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > watchAuctionSettled > forwards onError and polling options
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > claimIncentivesByPositionKey > resolves positionId then claims incentives
✓ test/unit/entities/OpeningAuction.test.ts > OpeningAuction > claimIncentivesByPositionKey > throws a friendly error when no position is found
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchBidStatus > polls getBidStatus on each block and calls onStatusChange on first emit
✓ test/unit/entities/DopplerSDK.openingAuctionPositionManager.test.ts > DopplerSDK opening auction helpers > resolves the configured position manager instance
✓ test/unit/entities/DopplerSDK.openingAuctionPositionManager.test.ts > DopplerSDK opening auction helpers > throws when the chain lacks a position manager address
✓ test/unit/entities/DopplerSDK.openingAuctionPositionManager.test.ts > DopplerSDK opening auction helpers > accepts an explicit address override
✓ test/unit/entities/DopplerSDK.openingAuctionLifecycle.test.ts > DopplerSDK opening auction lifecycle helpers > resolves the configured initializer instance
✓ test/unit/entities/DopplerSDK.openingAuctionLifecycle.test.ts > DopplerSDK opening auction lifecycle helpers > throws when the chain lacks an initializer address
✓ test/unit/entities/DopplerSDK.openingAuctionLifecycle.test.ts > DopplerSDK opening auction lifecycle helpers > accepts an explicit address override
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchBidStatus > does not call onStatusChange when status has not changed
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchBidStatus > calls onError when getBidStatus throws
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > OpeningAuctionBidManager > watchBidStatus > stops polling when unsubscribe is called
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > DopplerSDK.getOpeningAuctionBidManager > creates an OpeningAuctionBidManager via SDK entrypoint
✓ test/unit/entities/OpeningAuctionBidManager.test.ts > DopplerSDK.getOpeningAuctionBidManager > throws when no positionManagerAddress is available on chain

Test Files 6 passed (6)
Tests 164 passed (164)
Start at 16:48:41
Duration 1.72s (transform 567ms, setup 1.08s, collect 2.20s, tests 439ms, environment 1ms, prepare 446ms)\n - 6 files, 164 passing\n

@z80dev
Copy link
Collaborator Author

z80dev commented Feb 24, 2026

Follow-up update from dual-agent review (Claude + Codex):

  • Normalized tuple bigint/number fields in opening-auction reads (tickLower/tickUpper, lifecycle status).
  • Added positive-liquidity guards across bid place/withdraw paths to prevent invalid/inverted ops.
  • Added/expanded opening-auction unit coverage (including new OpeningAuctionBidManager suite) and liquidity math unit tests.
  • Improved owner-indexed position enumeration end-of-list handling to reduce unnecessary global scan fallback.
  • Added fork bid-management test coverage and stricter async handling in opening-auction fork smoke tests.
  • Exported missing opening-auction public types from barrel/root exports.

Validation run locally:

  • pnpm typecheck
  • pnpm vitest run --config vitest.unit.config.ts test/unit/entities/OpeningAuction.test.ts test/unit/entities/OpeningAuctionBidManager.test.ts test/unit/entities/OpeningAuctionPositionManager.test.ts test/unit/entities/OpeningAuctionLifecycle.test.ts test/unit/entities/DopplerSDK.openingAuctionLifecycle.test.ts test/unit/entities/DopplerSDK.openingAuctionPositionManager.test.ts
    • 6 files, 164 passing

- Fix invalid address fallback in event watchers (zeroHash.slice → zeroAddress)
- Consolidate phase/status constants into shared constants.ts
- Add error context to moveBid failures (includes withdraw tx hash)
- Replace Date.now() snapshot cache with block-number based approach
- Make incentive share estimation return null on failure instead of silent 0
- Extract gas estimation pattern to shared resolveGasEstimate utility
- Deduplicate INT24_MIN/INT24_MAX into shared constants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@z80dev
Copy link
Collaborator Author

z80dev commented Feb 25, 2026

Code Review — PR #84: Opening Auction Support for Base Sepolia

7 issues found (3 HIGH, 3 MEDIUM, 1 LOW) · All 506 unit tests pass · Typecheck clean


Issues

# Severity Location Problem Why Fix
1 HIGH OpeningAuction.ts:844-875 getOwnerPositionsByGlobalScan issues O(N) RPC calls (batched by 20) for every position in the system to find one user's positions For a popular auction with thousands of positions, this will cause thousands of RPC calls, rate-limiting, and potential timeouts. This is only a fallback path (after indexed enumeration fails), but it's still reachable. Use multicall to batch position reads into a single RPC call per chunk (e.g., 200 per multicall), or clearly document this as an O(N) last-resort method.
2 HIGH OpeningAuctionBidManager.ts:1039-1041 getTickLiquidityDistribution fires up to 1000 parallel RPC calls via Promise.all Public RPC nodes will rate-limit or reject this volume of simultaneous requests. Even paid nodes may return inconsistent data under burst load. Use multicall to batch all liquidityAtTick reads into one or a few RPC calls, or chunk into smaller batches with throttling.
3 HIGH OpeningAuctionBidManager.ts:1500-1508 watchBidStatus fires getBidStatus on every new block, which involves 6+ RPC reads per invocation On Base (2s blocks), this creates ~3 compound RPC calls/second continuously. For multiple watchers, this multiplies. No debounce/throttle mechanism. Throttle polling to every N blocks (e.g., 5) or add a configurable blockInterval option. The polling guard prevents concurrent flights, but not the frequency.
4 MEDIUM OpeningAuctionBidManager.ts:1240-1309 moveBid is non-atomic (withdraw + place). If withdraw succeeds but place fails, user's funds are in limbo between ticks. The error message is descriptive and includes recovery guidance, which is good. But frontends need to handle this edge case properly. No code change needed (well-documented), but consider adding this to the JSDoc as a @throws case so IDEs surface it, and mention it in the examples.
5 MEDIUM DopplerFactory.ts:2217 mineDopplerCompletionSalt checks bytecode at each candidate address via getBytecode RPC call in a loop up to 1M iterations While the loop is bounded by ONE_MILLION, each iteration does an RPC call. In practice the first salt usually works, but this could degrade badly if many hook addresses are taken. Consider mining deterministically (pure hash computation) to check flag bits first, then only call getBytecode once a valid candidate is found. This is already partially done via mineDopplerHookSalt which mines for flags, but the bytecode check adds an RPC per attempt.
6 MEDIUM DopplerFactory.ts:1184-1189 startTimeOffset and startingTime are read from both params and params.doppler with cascading fallback, but ResolvedOpeningAuctionDopplerConfig doesn't declare those fields The fallback params.doppler.startTimeOffset / params.doppler.startingTime will always be undefined since those fields don't exist on the type. Dead code that's confusing but harmless due to the final fallback to blockTimestamp + 30. Remove the dead params.doppler.startTimeOffset / params.doppler.startingTime fallbacks, or add those fields to the Doppler config type if intended.
7 LOW OpeningAuctionBidManager.ts:1507 onError: (error) => ... — parameter error implicitly has any type (TS7006 in strict mode) TypeScript strict mode will flag this. Passes now but is a code quality issue. Add explicit type: (error: Error) => ...

Positive Observations

  • ✅ All 506 unit tests pass, typecheck clean
  • ✅ Excellent validation in OpeningAuctionBuilder.build() — tick spacing divisibility, fee bounds, supply checks, governance chain compatibility
  • ✅ Single-tick enforcement in OpeningAuctionPositionManager.validateSingleTick prevents invalid multi-tick positions
  • ✅ Block-number-based snapshot cache in BidManager (much better than wall-clock time)
  • ✅ Positive-liquidity guards on all bid place/withdraw paths
  • ✅ Thorough error context in moveBid failure (includes withdraw tx hash for recovery)
  • ✅ Clean exports — all public types properly exported through barrel files and root index
  • ✅ Consistent patterns — follows existing SDK conventions (simulate → execute, wallet client guards, gas estimation)
  • ✅ Good test coverage — 164 opening auction tests across 6 test files, plus fork tests
  • resolveGasEstimate utility deduplicates a common pattern across entities

Overall: Well-structured, well-tested PR. The main concerns are around RPC call volume in position enumeration and tick liquidity queries — performance issues rather than correctness bugs.

…base-sepolia

# Conflicts:
#	src/addresses.ts
#	src/entities/DopplerFactory.ts
#	src/types.ts
@whetstone-devops-app
Copy link

whetstone-devops-app bot commented Feb 26, 2026

🔄 Address Changes Detected

Compared feat/opening-auction-base-sepolia with main

The following addresses used by the SDK have changed:

Note: This diff shows only the addresses actually used by the SDK (from ADDRESSES constant).

 {
   8453: {
+    openingAuctionInitializer: "0x0000000000000000000000000000000000000000"
+    openingAuctionPositionManager: "0x0000000000000000000000000000000000000000"
   }
   84532: {
+    openingAuctionInitializer: "0x3dCd35945Dc86a9FaA80846B06CB4676961d0AEa"
+    openingAuctionPositionManager: "0x957CA7472ced1C1B3608152F83E0E69F975a37a9"
   }
 }

Last checked: February 26, 2026 at 17:06:54 UTC

@Cooper-Kunz Cooper-Kunz requested a review from rustydotwtf March 12, 2026 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant