DopplerHookInitializer

0x4e3468951d49f2eea976ed0d6e75ffcb44a9a544

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
25,533 bytes
ABI entries
72
13 read · 20 write
License
none

Contract information

Address
0x4e3468951d49f2eea976ed0d6e75ffcb44a9a544
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xd32e8ebbb5…fcfbe700b1

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (13)

airlock()address
getAuthority(address)address
getBeneficiaries(address)tuple[]
getCumulatedFees0(bytes32)uint256
getCumulatedFees1(bytes32)uint256
getHookPermissions()tuple
getLastCumulatedFees0(bytes32, address)uint256
getLastCumulatedFees1(bytes32, address)uint256
getPoolKey(bytes32)address, address, uint24, int24, address
getShares(bytes32, address)uint256
getState(address)address, uint256, address, bytes, uint8, tuple, int24
isDopplerHookEnabled(address)uint256
poolManager()address

Events (11)

CollectCreateDelegateAuthorityGraduateLockModifyLiquidityReleaseSetDopplerHookSetDopplerHookStateSwapUpdateBeneficiary

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "airlock_",
        "type": "address"
      },
      {
        "internalType": "contract IPoolManager",
        "name": "poolManager_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "ArrayLengthsMismatch",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "int24",
        "name": "targetTick",
        "type": "int24"
      },
      {
        "internalType": "int24",
        "name": "currentTick",
        "type": "int24"
      }
    ],
    "name": "CannotMigrateInsufficientTick",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CannotMigratePoolNoProvidedDopplerHook",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "DopplerHookNotEnabled",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "HookNotImplemented",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint8",
        "name": "action",
        "type": "uint8"
      }
    ],
    "name": "InvalidCallbackAction",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidNewBeneficiary",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidProtocolOwnerBeneficiary",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint96",
        "name": "required",
        "type": "uint96"
      },
      {
        "internalType": "uint96",
        "name": "provided",
        "type": "uint96"
      }
    ],
    "name": "InvalidProtocolOwnerShares",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidShares",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidTotalShares",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidTotalShares",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint24",
        "name": "maxFee",
        "type": "uint24"
      },
      {
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }
    ],
    "name": "LPFeeTooHigh",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotPoolManager",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyInitializer",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Reentrancy",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SenderNotAirlock",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SenderNotAirlockOwner",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SenderNotAuthorized",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "int24",
        "name": "tick",
        "type": "int24"
      }
    ],
    "name": "TickNotAligned",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "int24",
        "name": "tickLower",
        "type": "int24"
      },
      {
        "internalType": "int24",
        "name": "tickUpper",
        "type": "int24"
      }
    ],
    "name": "TickRangeMisordered",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "UnorderedBeneficiaries",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "UnreachableFarTick",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "enum PoolStatus",
        "name": "expected",
        "type": "uint8"
      },
      {
        "internalType": "enum PoolStatus",
        "name": "actual",
        "type": "uint8"
      }
    ],
    "name": "WrongPoolStatus",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroPosition",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroShare",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "fees0",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "fees1",
        "type": "uint256"
      }
    ],
    "name": "Collect",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "poolOrHook",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "asset",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "numeraire",
        "type": "address"
      }
    ],
    "name": "Create",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "user",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "authority",
        "type": "address"
      }
    ],
    "name": "DelegateAuthority",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "asset",
        "type": "address"
      }
    ],
    "name": "Graduate",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "pool",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "address",
            "name": "beneficiary",
            "type": "address"
          },
          {
            "internalType": "uint96",
            "name": "shares",
            "type": "uint96"
          }
        ],
        "indexed": false,
        "internalType": "struct BeneficiaryData[]",
        "name": "beneficiaries",
        "type": "tuple[]"
      }
    ],
    "name": "Lock",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "indexed": false,
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "indexed": false,
        "internalType": "struct IPoolManager.ModifyLiquidityParams",
        "name": "params",
        "type": "tuple"
      }
    ],
    "name": "ModifyLiquidity",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "beneficiary",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "fees0",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "fees1",
        "type": "uint256"
      }
    ],
    "name": "Release",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "asset",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "dopplerHook",
        "type": "address"
      }
    ],
    "name": "SetDopplerHook",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "dopplerHook",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "flag",
        "type": "uint256"
      }
    ],
    "name": "SetDopplerHookState",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "indexed": true,
        "internalType": "struct PoolKey",
        "name": "poolKey",
        "type": "tuple"
      },
      {
        "indexed": true,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "components": [
          {
            "internalType": "bool",
            "name": "zeroForOne",
            "type": "bool"
          },
          {
            "internalType": "int256",
            "name": "amountSpecified",
            "type": "int256"
          },
          {
            "internalType": "uint160",
            "name": "sqrtPriceLimitX96",
            "type": "uint160"
          }
        ],
        "indexed": false,
        "internalType": "struct IPoolManager.SwapParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "indexed": false,
        "internalType": "int128",
        "name": "amount0",
        "type": "int128"
      },
      {
        "indexed": false,
        "internalType": "int128",
        "name": "amount1",
        "type": "int128"
      },
      {
        "indexed": false,
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "Swap",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "oldBeneficiary",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "newBeneficiary",
        "type": "address"
      }
    ],
    "name": "UpdateBeneficiary",
    "type": "event"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct IPoolManager.ModifyLiquidityParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "BalanceDelta",
        "name": "delta",
        "type": "int256"
      },
      {
        "internalType": "BalanceDelta",
        "name": "feesAccrued",
        "type": "int256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "afterAddLiquidity",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      },
      {
        "internalType": "BalanceDelta",
        "name": "",
        "type": "int256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "amount0",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount1",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "afterDonate",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      },
      {
        "internalType": "int24",
        "name": "tick",
        "type": "int24"
      }
    ],
    "name": "afterInitialize",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct IPoolManager.ModifyLiquidityParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "BalanceDelta",
        "name": "delta",
        "type": "int256"
      },
      {
        "internalType": "BalanceDelta",
        "name": "feesAccrued",
        "type": "int256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "afterRemoveLiquidity",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      },
      {
        "internalType": "BalanceDelta",
        "name": "",
        "type": "int256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "bool",
            "name": "zeroForOne",
            "type": "bool"
          },
          {
            "internalType": "int256",
            "name": "amountSpecified",
            "type": "int256"
          },
          {
            "internalType": "uint160",
            "name": "sqrtPriceLimitX96",
            "type": "uint160"
          }
        ],
        "internalType": "struct IPoolManager.SwapParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "BalanceDelta",
        "name": "delta",
        "type": "int256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "afterSwap",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      },
      {
        "internalType": "int128",
        "name": "",
        "type": "int128"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "airlock",
    "outputs": [
      {
        "internalType": "contract Airlock",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct IPoolManager.ModifyLiquidityParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "beforeAddLiquidity",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "amount0",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount1",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "beforeDonate",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      }
    ],
    "name": "beforeInitialize",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct IPoolManager.ModifyLiquidityParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "beforeRemoveLiquidity",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "bool",
            "name": "zeroForOne",
            "type": "bool"
          },
          {
            "internalType": "int256",
            "name": "amountSpecified",
            "type": "int256"
          },
          {
            "internalType": "uint160",
            "name": "sqrtPriceLimitX96",
            "type": "uint160"
          }
        ],
        "internalType": "struct IPoolManager.SwapParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "beforeSwap",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      },
      {
        "internalType": "BeforeSwapDelta",
        "name": "",
        "type": "int256"
      },
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "collectFees",
    "outputs": [
      {
        "internalType": "uint128",
        "name": "fees0",
        "type": "uint128"
      },
      {
        "internalType": "uint128",
        "name": "fees1",
        "type": "uint128"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "delegatedAuthority",
        "type": "address"
      }
    ],
    "name": "delegateAuthority",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "asset",
        "type": "address"
      }
    ],
    "name": "exitLiquidity",
    "outputs": [
      {
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      },
      {
        "internalType": "address",
        "name": "token0",
        "type": "address"
      },
      {
        "internalType": "uint128",
        "name": "fees0",
        "type": "uint128"
      },
      {
        "internalType": "uint128",
        "name": "balance0",
        "type": "uint128"
      },
      {
        "internalType": "address",
        "name": "token1",
        "type": "address"
      },
      {
        "internalType": "uint128",
        "name": "fees1",
        "type": "uint128"
      },
      {
        "internalType": "uint128",
        "name": "balance1",
        "type": "uint128"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "user",
        "type": "address"
      }
    ],
    "name": "getAuthority",
    "outputs": [
      {
        "internalType": "address",
        "name": "authority",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "asset",
        "type": "address"
      }
    ],
    "name": "getBeneficiaries",
    "outputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "beneficiary",
            "type": "address"
          },
          {
            "internalType": "uint96",
            "name": "shares",
            "type": "uint96"
          }
        ],
        "internalType": "struct BeneficiaryData[]",
        "name": "",
        "type": "tuple[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "getCumulatedFees0",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "cumulatedFees0",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "getCumulatedFees1",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "cumulatedFees1",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getHookPermissions",
    "outputs": [
      {
        "components": [
          {
            "internalType": "bool",
            "name": "beforeInitialize",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterInitialize",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeAddLiquidity",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterAddLiquidity",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeRemoveLiquidity",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterRemoveLiquidity",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeSwap",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterSwap",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeDonate",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterDonate",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeSwapReturnDelta",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterSwapReturnDelta",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterAddLiquidityReturnDelta",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterRemoveLiquidityReturnDelta",
            "type": "bool"
          }
        ],
        "internalType": "struct Hooks.Permissions",
        "name": "",
        "type": "tuple"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "beneficiary",
        "type": "address"
      }
    ],
    "name": "getLastCumulatedFees0",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "lastCumulatedFees0",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "beneficiary",
        "type": "address"
      }
    ],
    "name": "getLastCumulatedFees1",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "lastCumulatedFees1",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "getPoolKey",
    "outputs": [
      {
        "internalType": "Currency",
        "name": "currency0",
        "type": "address"
      },
      {
        "internalType": "Currency",
        "name": "currency1",
        "type": "address"
      },
      {
        "internalType": "uint24",
        "name": "fee",
        "type": "uint24"
      },
      {
        "internalType": "int24",
        "name": "tickSpacing",
        "type": "int24"
      },
      {
        "internalType": "contract IHooks",
        "name": "hooks",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "beneficiary",
        "type": "address"
      }
    ],
    "name": "getShares",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "shares",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "asset",
        "type": "address"
      }
    ],
    "name": "getState",
    "outputs": [
      {
        "internalType": "address",
        "name": "numeraire",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "totalTokensOnBondingCurve",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "dopplerHook",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "graduationDopplerHookCalldata",
        "type": "bytes"
      },
      {
        "internalType": "enum PoolStatus",
        "name": "status",
        "type": "uint8"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "poolKey",
        "type": "tuple"
      },
      {
        "internalType": "int24",
        "name": "farTick",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "asset",
        "type": "address"
      }
    ],
    "name": "graduate",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "asset",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "numeraire",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "totalTokensOnBondingCurve",
        "type": "uint256"
      },
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "initialize",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "dopplerHook",
        "type": "address"
      }
    ],
    "name": "isDopplerHookEnabled",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "flags",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolManager",
    "outputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "asset",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "dopplerHook",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "onInitializationCalldata",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "onGraduationCalldata",
        "type": "bytes"
      }
    ],
    "name": "setDopplerHook",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address[]",
        "name": "dopplerHooks",
        "type": "address[]"
      },
      {
        "internalType": "uint256[]",
        "name": "flags",
        "type": "uint256[]"
      }
    ],
    "name": "setDopplerHookState",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "unlockCallback",
    "outputs": [
      {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "newBeneficiary",
        "type": "address"
      }
    ],
    "name": "updateBeneficiary",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "asset",
        "type": "address"
      },
      {
        "internalType": "uint24",
        "name": "lpFee",
        "type": "uint24"
      }
    ],
    "name": "updateDynamicLPFee",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

import { SafeTransferLib } from "@solady/utils/SafeTransferLib.sol";
import { IHooks } from "@v4-core/interfaces/IHooks.sol";
import { IPoolManager } from "@v4-core/interfaces/IPoolManager.sol";
import { Hooks } from "@v4-core/libraries/Hooks.sol";
import { LPFeeLibrary } from "@v4-core/libraries/LPFeeLibrary.sol";
import { StateLibrary } from "@v4-core/libraries/StateLibrary.sol";
import { TickMath } from "@v4-core/libraries/TickMath.sol";
import { BalanceDelta, BalanceDeltaLibrary } from "@v4-core/types/BalanceDelta.sol";
import { Currency, CurrencyLibrary } from "@v4-core/types/Currency.sol";
import { PoolId } from "@v4-core/types/PoolId.sol";
import { PoolKey } from "@v4-core/types/PoolKey.sol";
import { ImmutableState } from "@v4-periphery/base/ImmutableState.sol";
import { ON_GRADUATION_FLAG, ON_INITIALIZATION_FLAG, ON_SWAP_FLAG } from "src/base/BaseDopplerHookInitializer.sol";
import { BaseHook } from "src/base/BaseHook.sol";
import { FeesManager } from "src/base/FeesManager.sol";
import { ImmutableAirlock, SenderNotAirlock } from "src/base/ImmutableAirlock.sol";
import { MiniV4Manager } from "src/base/MiniV4Manager.sol";
import { IDopplerHookInitializer } from "src/interfaces/IDopplerHookInitializer.sol";
import { IPoolInitializer } from "src/interfaces/IPoolInitializer.sol";
import { Curve, Multicurve } from "src/libraries/Multicurve.sol";
import { BeneficiaryData, MIN_PROTOCOL_OWNER_SHARES } from "src/types/BeneficiaryData.sol";
import { Position } from "src/types/Position.sol";

/**
 * @notice Emitted when a new pool is locked
 * @param pool Address of the Uniswap V4 pool key
 * @param beneficiaries Array of beneficiaries with their shares
 */
event Lock(address indexed pool, BeneficiaryData[] beneficiaries);

/**
 * @notice Emitted when the state of a Doppler Hook is set
 * @param dopplerHook Address of the Doppler Hook
 * @param flag Flag of the Doppler Hook (see flags in BaseDopplerHookInitializer.sol)
 */
event SetDopplerHookState(address indexed dopplerHook, uint256 indexed flag);

/// @notice Emitted when a dopplerHook is linked to a pool
event SetDopplerHook(address indexed asset, address indexed dopplerHook);

/// @notice Emitted when a pool graduates
event Graduate(address indexed asset);

/**
 * @notice Emitted when liquidity is modified
 * @param key Key of the related pool
 * @param params Parameters of the liquidity modification
 */
event ModifyLiquidity(PoolKey key, IPoolManager.ModifyLiquidityParams params);

/**
 * @notice Emitted when a Swap occurs
 * @param sender Address calling the PoolManager
 * @param poolKey Key of the related pool
 * @param poolId Id of the related pool
 * @param params Parameters of the swap
 * @param amount0 Balance denominated in token0
 * @param amount1 Balance denominated in token1
 * @param hookData Data passed to the hook
 */
event Swap(
    address indexed sender,
    PoolKey indexed poolKey,
    PoolId indexed poolId,
    IPoolManager.SwapParams params,
    int128 amount0,
    int128 amount1,
    bytes hookData
);

/**
 * @notice Emitted when a user delegates their pool authority to another address
 * @param user Address of the user delegating their authority
 * @param authority Address of the delegated authority
 */
event DelegateAuthority(address indexed user, address indexed authority);

/// @notice Thrown when the caller is not the Uniswap V4 Multicurve Initializer
error OnlyInitializer();

/**
 * @notice Thrown when the pool is not in the expected status
 * @param expected Expected pool status
 * @param actual Actual pool status
 */
error WrongPoolStatus(PoolStatus expected, PoolStatus actual);

/// @notice Thrown when the current tick is not sufficient to migrate
error CannotMigrateInsufficientTick(int24 targetTick, int24 currentTick);

/// @notice Thrown when the hook is not provided but migration is attempted
error CannotMigratePoolNoProvidedDopplerHook();

/// @notice Thrown when an unauthorized sender calls `setDopplerHookState()`
error SenderNotAirlockOwner();

/// @notice Thrown when an unauthorized sender tries to associate a Doppler Hook to a pool
error SenderNotAuthorized();

/// @notice Thrown when the given Doppler Hook is not enabled
error DopplerHookNotEnabled();

/// @notice Thrown when the lengths of two arrays do not match
error ArrayLengthsMismatch();

/// @notice Thrown when the far tick is unreachable
error UnreachableFarTick();

/// @notice Thrown when the provided LP fee is above the maximum allowed
error LPFeeTooHigh(uint24 maxFee, uint256 fee);

/**
 * @notice Data used to initialize the Uniswap V4 pool
 * @param fee Fee of the Uniswap V4 pool (capped at 1_000_000), use a dynamic fee if a Doppler Hook is provided
 * @param farTick Farthest tick that must be reached to allow exiting liquidity, will be flipped if necessary
 * @param tickSpacing Tick spacing for the Uniswap V4 pool
 * @param curves Array of curves to distribute liquidity across
 * @param beneficiaries Array of beneficiaries with their shares, will lock the pool if not empty
 * @param dopplerHook Address of the associated Doppler Hook
 * @param onInitializationDopplerHookCalldata Calldata passed to the Doppler Hook on initialization
 * @param graduationDopplerHookCalldata Calldata passed to the Doppler Hook on graduation
 */
struct InitData {
    uint24 fee;
    int24 tickSpacing;
    int24 farTick;
    Curve[] curves;
    BeneficiaryData[] beneficiaries;
    address dopplerHook;
    bytes onInitializationDopplerHookCalldata;
    bytes graduationDopplerHookCalldata;
}

/// @notice Possible status of a pool, note a locked pool cannot be exited
enum PoolStatus {
    Uninitialized,
    Initialized,
    Locked,
    Graduated,
    Exited
}

/**
 * @notice State of a pool
 * @param numeraire Address of the numeraire currency
 * @param beneficiaries Array of beneficiaries with their shares
 * @param adjustedCurves Array of adjusted curves used for liquidity distribution
 * @param totalTokensOnBondingCurve Total amount of tokens allocated to the bonding curve
 * @param dopplerHook Address of the Doppler hook
 * @param graduationDopplerHookCalldata Calldata passed to the Doppler Hook on graduation
 * @param status Current status of the pool
 * @param poolKey Key of the Uniswap V4 pool
 * @param farTick Farthest tick that must be reached to allow exiting liquidity
 */
struct PoolState {
    address numeraire;
    BeneficiaryData[] beneficiaries;
    Curve[] adjustedCurves;
    uint256 totalTokensOnBondingCurve;
    address dopplerHook;
    bytes graduationDopplerHookCalldata;
    PoolStatus status;
    PoolKey poolKey;
    int24 farTick;
}

/// @dev Maximum LP fee allowed (1_000_000 = 100%)
uint24 constant MAX_LP_FEE = 100_000;

/**
 * @title Doppler Hook Uniswap V4 Multicurve Initializer
 * @author Whetstone Research
 * @custom:security-contact security@whetstone.cc
 * @notice Initializes a Uniswap V4 pool and distributes liquidity across multiple positions, as described
 * in the Doppler Multicurve whitepaper (https://www.doppler.lol/multicurve.pdf), with optional support for
 * Doppler Hooks for dynamic fee adjustment and custom logic on initialization, swaps and pool graduation
 */
contract DopplerHookInitializer is ImmutableAirlock, BaseHook, MiniV4Manager, FeesManager, IPoolInitializer {
    using StateLibrary for IPoolManager;
    using CurrencyLibrary for Currency;
    using BalanceDeltaLibrary for BalanceDelta;

    /// @notice Returns the state of a pool
    mapping(address asset => PoolState state) public getState;

    /// @notice Maps a Uniswap V4 poolId to its associated asset
    mapping(PoolId poolId => address asset) internal getAsset;

    /// @notice Returns a non-zero value if a Doppler hook is enabled
    mapping(address dopplerHook => uint256 flags) public isDopplerHookEnabled;

    /// @notice Returns the delegated authority for a user
    mapping(address user => address authority) public getAuthority;

    /**
     * @dev Since both the `MiniV4Manager` and the `BaseHook` contracts are inheriting from the `ImmutableState` base
     * contract we modified these contracts to get rid of their constructors, allowing us to call the base contract here
     * and avoid the `Base constructor arguments given twice` error due to both parents inheriting from the same child
     * @param airlock_ Address of the Airlock contract
     * @param poolManager_ Address of the Uniswap V4 pool manager
     */
    constructor(address airlock_, IPoolManager poolManager_) ImmutableAirlock(airlock_) ImmutableState(poolManager_) { }

    /// @inheritdoc IPoolInitializer
    function initialize(
        address asset,
        address numeraire,
        uint256 totalTokensOnBondingCurve,
        bytes32,
        bytes calldata data
    ) external override returns (address) {
        require(msg.sender == address(airlock), SenderNotAirlock());
        require(
            getState[asset].status == PoolStatus.Uninitialized,
            WrongPoolStatus(PoolStatus.Uninitialized, getState[asset].status)
        );

        InitData memory initData = abi.decode(data, (InitData));
        (PoolKey memory poolKey, Position[] memory positions) =
            _initialize(asset, numeraire, totalTokensOnBondingCurve, initData);

        PoolId poolId = poolKey.toId();
        getAsset[poolId] = asset;

        (
            address dopplerHook,
            uint24 fee,
            BeneficiaryData[] memory beneficiaries,
            bytes memory onInitializationDopplerHookCalldata
        ) = (initData.dopplerHook, initData.fee, initData.beneficiaries, initData.onInitializationDopplerHookCalldata);
        uint256 dopplerHookFlag = isDopplerHookEnabled[initData.dopplerHook];

        if (dopplerHook != address(0)) {
            require(dopplerHookFlag > 0, DopplerHookNotEnabled());
            poolManager.updateDynamicLPFee(poolKey, fee);
        }

        SafeTransferLib.safeTransferFrom(asset, address(airlock), address(this), totalTokensOnBondingCurve);
        _mint(poolKey, positions);

        emit Create(address(poolManager), asset, numeraire);

        if (beneficiaries.length != 0) {
            _storeBeneficiaries(poolKey, beneficiaries, airlock.owner(), MIN_PROTOCOL_OWNER_SHARES);
            emit Lock(asset, beneficiaries);
        }

        // If any dust asset tokens are left in this contract after providing liquidity, we send them
        // back to the Airlock so they'll be transferred to the associated governance or burnt
        if (Currency.wrap(asset).balanceOfSelf() > 0) {
            Currency.wrap(asset).transfer(address(airlock), Currency.wrap(asset).balanceOfSelf());
        }

        if (dopplerHookFlag & ON_INITIALIZATION_FLAG != 0) {
            IDopplerHookInitializer(dopplerHook).onInitialization(asset, poolKey, onInitializationDopplerHookCalldata);
        }

        // Uniswap V4 pools don't have addresses, so we are returning the asset address
        // instead to retrieve the associated state later during the `exitLiquidity` call
        return asset;
    }

    /// @dev Internal function to avoid stack too deep errors
    function _initialize(
        address asset,
        address numeraire,
        uint256 totalTokensOnBondingCurve,
        InitData memory initData
    ) private returns (PoolKey memory poolKey, Position[] memory positions) {
        poolKey = PoolKey({
            currency0: asset < numeraire ? Currency.wrap(asset) : Currency.wrap(numeraire),
            currency1: asset < numeraire ? Currency.wrap(numeraire) : Currency.wrap(asset),
            hooks: IHooks(address(this)),
            fee: initData.dopplerHook != address(0) ? LPFeeLibrary.DYNAMIC_FEE_FLAG : initData.fee,
            tickSpacing: initData.tickSpacing
        });
        bool isToken0 = asset == Currency.unwrap(poolKey.currency0);

        (Curve[] memory adjustedCurves, int24 lowerTickBoundary, int24 upperTickBoundary) =
            Multicurve.adjustCurves(initData.curves, 0, initData.tickSpacing, isToken0);

        int24 startTick;

        if (isToken0) {
            startTick = lowerTickBoundary;
            require(initData.farTick >= startTick && initData.farTick < upperTickBoundary, UnreachableFarTick());
        } else {
            startTick = upperTickBoundary;
            initData.farTick = -initData.farTick;
            require(initData.farTick <= startTick && initData.farTick >= lowerTickBoundary, UnreachableFarTick());
        }

        uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(startTick);
        poolManager.initialize(poolKey, sqrtPriceX96);

        positions =
            Multicurve.calculatePositions(adjustedCurves, initData.tickSpacing, totalTokensOnBondingCurve, 0, isToken0);

        PoolState memory state = PoolState({
            numeraire: numeraire,
            beneficiaries: initData.beneficiaries,
            adjustedCurves: adjustedCurves,
            totalTokensOnBondingCurve: totalTokensOnBondingCurve,
            dopplerHook: initData.dopplerHook,
            graduationDopplerHookCalldata: initData.graduationDopplerHookCalldata,
            status: initData.beneficiaries.length != 0 ? PoolStatus.Locked : PoolStatus.Initialized,
            poolKey: poolKey,
            farTick: initData.farTick
        });

        getState[asset] = state;
    }

    /// @inheritdoc IPoolInitializer
    function exitLiquidity(address asset)
        external
        returns (
            uint160 sqrtPriceX96,
            address token0,
            uint128 fees0,
            uint128 balance0,
            address token1,
            uint128 fees1,
            uint128 balance1
        )
    {
        require(msg.sender == address(airlock), SenderNotAirlock());
        PoolState memory state = getState[asset];
        require(state.status == PoolStatus.Initialized, WrongPoolStatus(PoolStatus.Initialized, state.status));
        getState[asset].status = PoolStatus.Exited;

        token0 = Currency.unwrap(state.poolKey.currency0);
        token1 = Currency.unwrap(state.poolKey.currency1);

        _canGraduateOrMigrate(state.poolKey.toId(), asset == token0, state.farTick);
        sqrtPriceX96 = TickMath.getSqrtPriceAtTick(state.farTick);

        Position[] memory positions = Multicurve.calculatePositions(
            state.adjustedCurves, state.poolKey.tickSpacing, state.totalTokensOnBondingCurve, 0, asset == token0
        );
        (BalanceDelta balanceDelta, BalanceDelta feesAccrued) = _burn(state.poolKey, positions);
        balance0 = uint128(balanceDelta.amount0());
        balance1 = uint128(balanceDelta.amount1());
        fees0 = uint128(feesAccrued.amount0());
        fees1 = uint128(feesAccrued.amount1());

        state.poolKey.currency0.transfer(msg.sender, balance0);
        state.poolKey.currency1.transfer(msg.sender, balance1);
    }

    /**
     * @notice Delegates `msg.sender`'s pool authority to another address
     * @param delegatedAuthority Address to delgate to
     */
    function delegateAuthority(address delegatedAuthority) external {
        emit DelegateAuthority(msg.sender, delegatedAuthority);
        getAuthority[msg.sender] = delegatedAuthority;
    }

    /**
     * @notice Associates a Doppler hook with the pool of a given asset
     * @param asset Address to of the targeted asset
     * @param dopplerHook Address of the Doppler hook being associated
     * @param onInitializationCalldata Calldata passed to the Doppler Hook on initialization
     * @param onGraduationCalldata Calldata passed to the Doppler Hook on graduation
     */
    function setDopplerHook(
        address asset,
        address dopplerHook,
        bytes calldata onInitializationCalldata,
        bytes calldata onGraduationCalldata
    ) external {
        PoolState memory state = getState[asset];
        require(state.status == PoolStatus.Locked, WrongPoolStatus(PoolStatus.Locked, state.status));

        (, address timelock,,,,,,,,) = airlock.getAssetData(asset);
        address authority = getAuthority[timelock];
        require(msg.sender == authority || msg.sender == timelock, SenderNotAuthorized());

        uint256 dopplerHookFlag = isDopplerHookEnabled[dopplerHook];

        if (dopplerHook != address(0)) {
            require(dopplerHookFlag > 0, DopplerHookNotEnabled());
        }

        getState[asset].dopplerHook = dopplerHook;
        getState[asset].graduationDopplerHookCalldata = onGraduationCalldata;
        emit SetDopplerHook(asset, dopplerHook);

        if (dopplerHookFlag & ON_INITIALIZATION_FLAG != 0) {
            IDopplerHookInitializer(dopplerHook).onInitialization(asset, state.poolKey, onInitializationCalldata);
        }
    }

    /**
     * @notice Graduates a pool if the conditions are met, this is a one-way operation
     * @param asset Address of the asset to graduate
     */
    function graduate(address asset) external {
        PoolState memory state = getState[asset];
        require(state.status == PoolStatus.Locked, WrongPoolStatus(PoolStatus.Locked, state.status));

        address dopplerHook = state.dopplerHook;
        uint256 flags = isDopplerHookEnabled[dopplerHook];
        if (dopplerHook == address(0) || flags & ON_GRADUATION_FLAG == 0) {
            revert CannotMigratePoolNoProvidedDopplerHook();
        }

        _canGraduateOrMigrate(state.poolKey.toId(), asset == Currency.unwrap(state.poolKey.currency0), state.farTick);

        getState[asset].status = PoolStatus.Graduated;
        emit Graduate(asset);

        IDopplerHookInitializer(dopplerHook).onGraduation(asset, state.poolKey, state.graduationDopplerHookCalldata);
    }

    /**
     * @notice Updates the LP fee for a given asset's pool
     * @param asset Address of the asset used for the Uniswap V4 pool
     * @param lpFee New dynamic LP fee to set
     */
    function updateDynamicLPFee(address asset, uint24 lpFee) external {
        PoolState memory state = getState[asset];
        require(state.status == PoolStatus.Locked, WrongPoolStatus(PoolStatus.Locked, state.status));
        require(msg.sender == state.dopplerHook, SenderNotAuthorized());
        require(lpFee <= MAX_LP_FEE, LPFeeTooHigh(MAX_LP_FEE, lpFee));
        poolManager.updateDynamicLPFee(state.poolKey, lpFee);
    }

    /**
     * @notice Sets the state of a given Doppler hooks array
     * @param dopplerHooks Array of Doppler hook addresses
     * @param flags Array of flags to set (see flags in BaseDopplerHookInitializer.sol)
     */
    function setDopplerHookState(address[] calldata dopplerHooks, uint256[] calldata flags) external {
        require(msg.sender == airlock.owner(), SenderNotAirlockOwner());

        uint256 length = dopplerHooks.length;

        if (length != flags.length) {
            revert ArrayLengthsMismatch();
        }

        for (uint256 i; i != length; i++) {
            isDopplerHookEnabled[dopplerHooks[i]] = flags[i];
            emit SetDopplerHookState(dopplerHooks[i], flags[i]);
        }
    }

    /**
     * @notice Returns the positions currently held in the Uniswap V4 pool for the given `asset`
     * @param asset Address of the asset used for the Uniswap V4 pool
     * @return Array of positions currently held in the Uniswap V4 pool
     */
    function getPositions(address asset) internal view returns (Position[] memory) {
        PoolState memory state = getState[asset];
        address token0 = Currency.unwrap(state.poolKey.currency0);
        Position[] memory positions = Multicurve.calculatePositions(
            state.adjustedCurves, state.poolKey.tickSpacing, state.totalTokensOnBondingCurve, 0, asset == token0
        );
        return positions;
    }

    /**
     * @notice Returns the beneficiaries and their shares for the given `asset`
     * @param asset Address of the asset used for the Uniswap V4 pool
     * @return Array of beneficiaries with their shares
     */
    function getBeneficiaries(address asset) external view returns (BeneficiaryData[] memory) {
        return getState[asset].beneficiaries;
    }

    /// @inheritdoc FeesManager
    function _collectFees(PoolId poolId) internal override returns (BalanceDelta fees) {
        address asset = getAsset[poolId];
        PoolState memory state = getState[asset];
        require(state.status == PoolStatus.Locked, WrongPoolStatus(PoolStatus.Locked, state.status));
        fees = _collect(state.poolKey, getPositions(asset));
    }

    /**
     * @notice Returns true if a pool can be graduated or migrated
     * @param poolId PoolId of the Uniswap V4 pool
     * @param isToken0 True if the asset is token0 in the pool
     * @param farTick The farthest tick that must be reached to allow exiting liquidity
     */
    function _canGraduateOrMigrate(PoolId poolId, bool isToken0, int24 farTick) internal view {
        (, int24 tick,,) = poolManager.getSlot0(poolId);
        require(isToken0 ? tick >= farTick : tick <= farTick, CannotMigrateInsufficientTick(farTick, tick));
    }

    /// @inheritdoc BaseHook
    function _beforeInitialize(address sender, PoolKey calldata, uint160) internal view override returns (bytes4) {
        require(sender == address(this), OnlyInitializer());
        return BaseHook.beforeInitialize.selector;
    }

    /// @inheritdoc BaseHook
    function _afterAddLiquidity(
        address,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        BalanceDelta,
        BalanceDelta,
        bytes calldata
    ) internal override returns (bytes4, BalanceDelta) {
        emit ModifyLiquidity(key, params);
        return (BaseHook.afterAddLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);
    }

    /// @inheritdoc BaseHook
    function _afterRemoveLiquidity(
        address,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        BalanceDelta,
        BalanceDelta,
        bytes calldata
    ) internal override returns (bytes4, BalanceDelta) {
        emit ModifyLiquidity(key, params);
        return (BaseHook.afterRemoveLiquidity.selector, BalanceDeltaLibrary.ZERO_DELTA);
    }

    /// @inheritdoc BaseHook
    function _afterSwap(
        address sender,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        BalanceDelta balanceDelta,
        bytes calldata data
    ) internal override returns (bytes4, int128) {
        address asset = getAsset[key.toId()];
        PoolState memory state = getState[asset];
        address dopplerHook = state.dopplerHook;

        int128 delta;

        if (dopplerHook != address(0) && isDopplerHookEnabled[dopplerHook] & ON_SWAP_FLAG != 0) {
            Currency feeCurrency;
            (feeCurrency, delta) = IDopplerHookInitializer(dopplerHook).onSwap(sender, key, params, balanceDelta, data);

            if (delta > 0) {
                poolManager.take(feeCurrency, address(this), uint128(delta));
                poolManager.sync(feeCurrency);

                if (feeCurrency.isAddressZero()) {
                    poolManager.settleFor{ value: uint128(delta) }(dopplerHook);
                } else {
                    feeCurrency.transfer(address(poolManager), uint128(delta));
                    poolManager.settleFor(dopplerHook);
                }
            }
        }

        emit Swap(sender, key, key.toId(), params, balanceDelta.amount0(), balanceDelta.amount1(), data);
        return (BaseHook.afterSwap.selector, delta);
    }

    /// @inheritdoc BaseHook
    function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
        return Hooks.Permissions({
            beforeInitialize: true,
            afterInitialize: false,
            beforeAddLiquidity: false,
            beforeRemoveLiquidity: false,
            afterAddLiquidity: true,
            afterRemoveLiquidity: true,
            beforeSwap: false,
            afterSwap: true,
            beforeDonate: false,
            afterDonate: false,
            beforeSwapReturnDelta: false,
            afterSwapReturnDelta: true,
            afterAddLiquidityReturnDelta: false,
            afterRemoveLiquidityReturnDelta: false
        });
    }
}
Chain explorer4227msChain node87ms