Doppler on Base

Uniswap v4 Hook Contract 0xdbe338085a102450abee805f2eb7531743cab8e0 on Base · Launchpad · Verified contract

Volume (24h)
$0
Value locked (TVL)
$40.15M
All-time volume
$0
Pools using this hook
1
Swaps (24h)
0 (0 traders)
Total swaps
0

What it does

Doppler is a Dutch-auction style token launch hook that sells a fixed token supply along a bonding curve that moves down over time, with liquidity managed automatically by the hook for traders and LPs.

This read was written for the hook 0x20a2…f8e0 on Base and reused here: the two run the same code.

How it works step by step

  • Accepts a fixed supply of tokens and sells them via a dynamic Dutch-auction curve with epoch-based rebalancing over the sale window.
  • Places lower, upper and price-discovery liquidity slugs held by the hook inside the PoolManager.
  • Tracks tokens sold and proceeds on every swap and triggers early exit when maximumProceeds is reached.
  • Handles maturity: if minimumProceeds is met it allows migration, otherwise it repositions all proceeds and restricts swaps to one direction.
  • Blocks external liquidity additions and donations so only the hook controls pool liquidity.
  • Allows the initializer to migrate all liquidity and balances to a recipient after the sale completes or exits early.
Swap behaviour
Swaps are gated: they revert before startingTime and after earlyExit. In beforeSwap the hook rebalances the curve once per epoch and, after maturity without enough proceeds, restricts swaps to one direction and sets fee to 0. In afterSwap it updates totalTokensSold and totalProceeds net of fees and re-centers price if it drifts out of range, triggering early exit when maximumProceeds is reached.
Liquidity behaviour
Liquidity can only be added by the hook itself (beforeAddLiquidity reverts unless caller is the hook). Positions are split into lower, upper and price-discovery slugs (salts 1 to 3+) and are cleared and re-placed on each epoch rebalance. Donations are blocked. Liquidity is removed only during migration.
Fee behaviour
Uses a dynamic fee flag in afterInitialize with the constructor-set initialLpFee. During insufficient-proceeds swaps in beforeSwap the fee is overridden to 0 for the asset-to-numeraire direction. Protocol fee is read from slot0 and used only for accounting of tokens sold and proceeds in afterSwap, not transferred to any party.
Owner & admin controls
No owner or pause role. Only initializer (the Airlock/launch contract, set at deployment) can call migrate(recipient), which is gated by sale maturity or early exit and withdraws all hook-held liquidity and balances to the recipient. isInitialized and poolKey are set once in beforeInitialize. Pool fee is set once via initialLpFee; no fee change path is exposed.

Enabled Uniswap v4 callbacks

beforeInitialize, afterInitialize, beforeAddLiquidity, beforeSwap, afterSwap, beforeDonate

beforeInitialize: records poolKey, enforces tick spacing and gamma divisibility, one-time initialization. afterInitialize: sets dynamic LP fee and places initial liquidity slugs via unlock callback. beforeAddLiquidity: blocks external liquidity provision. beforeSwap: starts sale at startingTime, rebalances the Dutch-auction curve per epoch, triggers insufficient-proceeds handling after maturity, enforces directional restrictions after maturity. afterSwap: tracks totalTokensSold and totalProceeds, re-centers the price if it drifts out of range, sets earlyExit at maximumProceeds. beforeDonate: always reverts.

Security & trust assessment

Risk level: medium (verified source, high confidence).

Who controls it: The only privileged address is the initializer, which can trigger migrate() once the sale has matured or early exit is reached, sending all pool-held assets to the recipient it names. Because the caller supplies the recipient, the initializer's upstream contract (Airlock) determines where funds go. No owner can change fees, pause swaps, or alter curve parameters after deployment.

Risk to user funds: Pool and sale proceeds are held by the hook as v4 liquidity and are moved during each epoch rebalance and swap. Traders interact with a curve the hook controls, but cannot be blocked by an admin key. Funds leave the pool only through migrate() by the initializer after maturity or early exit, which sends everything to the recipient it specifies.

Security findings

  • [info] Hook holds and moves pool liquidity by design: The hook holds all sale liquidity directly in the PoolManager under its own address and can move it on every swap and rebalance by calling poolManager.swap and modifyLiquidity, including forced re-centering swaps inside afterSwap.
  • [medium] Migration sends all funds to a caller-supplied recipient: migrate() can be called by the initializer once maturity or early exit is reached and transfers all position liquidity plus any stray balances to the recipient supplied by the caller. Correctness depends on the initializer contract (Airlock) passing the correct recipient.
  • [low] Insufficient-proceeds path changes swap direction rules: If the minimum proceeds are not met, afterMaturity swaps are restricted to one direction and all remaining numeraire is repositioned at an average clearing price; the swap restriction and fee override of 0 are enforced in beforeSwap, so the curve behavior is determined entirely by stored state.
  • [low] Complex tick math drives curve correctness: The lower slug range and rebalancing depend on tick arithmetic, rounding and epoch counts computed from constructor parameters; invalid parameters are guarded by constructor checks but edge cases in tick alignment were handled with explicit adjustments.
  • [info] No pause, owner or upgrade mechanism: Only the initializer can call migrate and the hook has no upgrade path, owner or pause function, so there is no admin key that can freeze or redirect trading.

Contract details

Verified source
Yes (Doppler)
Compiler
solc 0.8.26+commit.8a97fa7a
Bytecode size
21,832 bytes
Upgradeable proxy
No
Detected patterns
dynamic-fee, erc20, launchpad, liquidity-management, moves-currency, self-unlock

Pools using Doppler