QuiverV4Hook
0x1e4798985a823c0b4a6895b4a0c9a0f645e3a880
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
9,449 bytes
ABI entries
51
19 read · 11 write
License
none
Contract information
- Address
- 0x1e4798985a823c0b4a6895b4a0c9a0f645e3a880
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x69A4415006…b2E196D854
- Creation tx
- 0xa647cbe912…d583c965fe
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (19)
MAX_VIEW_PAGE() → uint256
REQUIRED_FLAGS() → uint160
afterAddLiquidity(address, tuple, tuple, int256, int256, bytes) → bytes4, int256
afterDonate(address, tuple, uint256, uint256, bytes) → bytes4
afterInitialize(address, tuple, uint160, int24) → bytes4
afterRemoveLiquidity(address, tuple, tuple, int256, int256, bytes) → bytes4, int256
afterSwap(address, tuple, tuple, int256, bytes) → bytes4, int128
beforeDonate(address, tuple, uint256, uint256, bytes) → bytes4
beforeRemoveLiquidity(address, tuple, tuple, bytes) → bytes4
gate() → address
launcher() → address
owner() → address
pendingOwner() → address
poolConfigs(bytes32) → address, address, uint160, bool, bool, bool, bool, bool
poolConfigsBatch(bytes32[]) → tuple[]
poolManager() → address
positionManager() → address
quoter() → address
universalRouter() → address
Events (7)
BootstrapClosedGatingEndedLauncherSetLiquidityExclusivityDisabledOwnershipTransferStartedOwnershipTransferredPoolRegistered
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "poolManager_",
"type": "address"
},
{
"internalType": "address",
"name": "positionManager_",
"type": "address"
},
{
"internalType": "address",
"name": "universalRouter_",
"type": "address"
},
{
"internalType": "address",
"name": "quoter_",
"type": "address"
},
{
"internalType": "address",
"name": "gate_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "BuyNotAuthorized",
"type": "error"
},
{
"inputs": [],
"name": "InvalidHookAddress",
"type": "error"
},
{
"inputs": [],
"name": "InvalidInitialization",
"type": "error"
},
{
"inputs": [],
"name": "InvalidPool",
"type": "error"
},
{
"inputs": [],
"name": "LauncherAlreadySet",
"type": "error"
},
{
"inputs": [],
"name": "LiquidityNotAuthorized",
"type": "error"
},
{
"inputs": [],
"name": "OnlyCreator",
"type": "error"
},
{
"inputs": [],
"name": "OnlyLauncher",
"type": "error"
},
{
"inputs": [],
"name": "OnlyPoolManager",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"inputs": [],
"name": "PageTooLarge",
"type": "error"
},
{
"inputs": [],
"name": "UnsupportedHook",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"name": "BootstrapClosed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"name": "GatingEnded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "launcher",
"type": "address"
}
],
"name": "LauncherSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"name": "LiquidityExclusivityDisabled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferStarted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": false,
"internalType": "uint160",
"name": "initialSqrtPriceX96",
"type": "uint160"
},
{
"indexed": false,
"internalType": "bool",
"name": "gated",
"type": "bool"
},
{
"indexed": false,
"internalType": "bool",
"name": "liquidityExclusive",
"type": "bool"
}
],
"name": "PoolRegistered",
"type": "event"
},
{
"inputs": [],
"name": "MAX_VIEW_PAGE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "REQUIRED_FLAGS",
"outputs": [
{
"internalType": "uint160",
"name": "",
"type": "uint160"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"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": "",
"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 ModifyLiquidityParams",
"name": "",
"type": "tuple"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "afterAddLiquidity",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"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": "",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "afterDonate",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"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": "",
"type": "tuple"
},
{
"internalType": "uint160",
"name": "",
"type": "uint160"
},
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"name": "afterInitialize",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"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": "",
"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 ModifyLiquidityParams",
"name": "",
"type": "tuple"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "afterRemoveLiquidity",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"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": "",
"type": "tuple"
},
{
"components": [
{
"internalType": "bool",
"name": "zeroForOne",
"type": "bool"
},
{
"internalType": "int256",
"name": "amountSpecified",
"type": "int256"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct SwapParams",
"name": "",
"type": "tuple"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "afterSwap",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
},
{
"internalType": "int128",
"name": "",
"type": "int128"
}
],
"stateMutability": "pure",
"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 ModifyLiquidityParams",
"name": "",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "beforeAddLiquidity",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"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": "",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "beforeDonate",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"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": "",
"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": "",
"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 ModifyLiquidityParams",
"name": "",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "beforeRemoveLiquidity",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"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 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": "closeBootstrap",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"name": "disableLiquidityExclusivity",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"name": "endGating",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "gate",
"outputs": [
{
"internalType": "contract IQuiverV4GateAuthorization",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launcher",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingOwner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "PoolId",
"name": "",
"type": "bytes32"
}
],
"name": "poolConfigs",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint160",
"name": "initialSqrtPriceX96",
"type": "uint160"
},
{
"internalType": "bool",
"name": "registered",
"type": "bool"
},
{
"internalType": "bool",
"name": "initialized",
"type": "bool"
},
{
"internalType": "bool",
"name": "gated",
"type": "bool"
},
{
"internalType": "bool",
"name": "liquidityExclusive",
"type": "bool"
},
{
"internalType": "bool",
"name": "bootstrapActive",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "PoolId[]",
"name": "poolIds",
"type": "bytes32[]"
}
],
"name": "poolConfigsBatch",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint160",
"name": "initialSqrtPriceX96",
"type": "uint160"
},
{
"internalType": "bool",
"name": "registered",
"type": "bool"
},
{
"internalType": "bool",
"name": "initialized",
"type": "bool"
},
{
"internalType": "bool",
"name": "gated",
"type": "bool"
},
{
"internalType": "bool",
"name": "liquidityExclusive",
"type": "bool"
},
{
"internalType": "bool",
"name": "bootstrapActive",
"type": "bool"
}
],
"internalType": "struct QuiverV4Hook.PoolConfig[]",
"name": "configs",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolManager",
"outputs": [
{
"internalType": "contract IPoolManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionManager",
"outputs": [
{
"internalType": "contract IPositionManagerSender",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "quoter",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"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"
}
],
"internalType": "struct PoolKey",
"name": "key",
"type": "tuple"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint160",
"name": "initialSqrtPriceX96",
"type": "uint160"
},
{
"internalType": "bool",
"name": "gated",
"type": "bool"
},
{
"internalType": "bool",
"name": "liquidityExclusive",
"type": "bool"
}
],
"name": "registerPool",
"outputs": [
{
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "launcher_",
"type": "address"
}
],
"name": "setLauncher",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "universalRouter",
"outputs": [
{
"internalType": "contract IUniversalRouterSender",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2026 Quiver (quiver-app.io)
pragma solidity 0.8.26;
import { Ownable2Step } from "openzeppelin-contracts/contracts/access/Ownable2Step.sol";
import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol";
import { IPoolManager } from "v4-core/src/interfaces/IPoolManager.sol";
import { IHooks } from "v4-core/src/interfaces/IHooks.sol";
import { Hooks } from "v4-core/src/libraries/Hooks.sol";
import { PoolKey } from "v4-core/src/types/PoolKey.sol";
import { PoolId, PoolIdLibrary } from "v4-core/src/types/PoolId.sol";
import { Currency } from "v4-core/src/types/Currency.sol";
import { BalanceDelta } from "v4-core/src/types/BalanceDelta.sol";
import { BeforeSwapDelta, BeforeSwapDeltaLibrary } from "v4-core/src/types/BeforeSwapDelta.sol";
import { ModifyLiquidityParams, SwapParams } from "v4-core/src/types/PoolOperation.sol";
interface IPositionManagerSender {
function msgSender() external view returns (address);
}
interface IUniversalRouterSender {
function msgSender() external view returns (address);
}
interface IQuiverV4GateAuthorization {
function isAuthorized(address buyer, PoolId poolId, uint256 nonce) external view returns (bool);
function lockPoolSigner(PoolId poolId) external;
}
/// @notice Shared Quiver v4 policy hook. Every V2 pool uses this hook.
contract QuiverV4Hook is IHooks, Ownable2Step {
using PoolIdLibrary for PoolKey;
uint160 public constant REQUIRED_FLAGS = 0x2880;
uint256 public constant MAX_VIEW_PAGE = 500;
struct PoolConfig {
address token;
address creator;
uint160 initialSqrtPriceX96;
bool registered;
bool initialized;
bool gated;
bool liquidityExclusive;
bool bootstrapActive;
}
IPoolManager public immutable poolManager;
IPositionManagerSender public immutable positionManager;
IUniversalRouterSender public immutable universalRouter;
address public immutable quoter;
IQuiverV4GateAuthorization public immutable gate;
address public launcher;
mapping(PoolId => PoolConfig) public poolConfigs;
event LauncherSet(address indexed launcher);
event PoolRegistered(
PoolId indexed poolId,
address indexed token,
address indexed creator,
uint160 initialSqrtPriceX96,
bool gated,
bool liquidityExclusive
);
event BootstrapClosed(PoolId indexed poolId);
event GatingEnded(PoolId indexed poolId);
event LiquidityExclusivityDisabled(PoolId indexed poolId);
error OnlyPoolManager();
error OnlyLauncher();
error OnlyCreator();
error LauncherAlreadySet();
error InvalidHookAddress();
error InvalidPool();
error InvalidInitialization();
error LiquidityNotAuthorized();
error BuyNotAuthorized();
error UnsupportedHook();
error PageTooLarge();
constructor(
address poolManager_,
address positionManager_,
address universalRouter_,
address quoter_,
address gate_
) Ownable(msg.sender) {
if (
poolManager_ == address(0) || positionManager_ == address(0) || universalRouter_ == address(0)
|| quoter_ == address(0) || gate_ == address(0)
) revert InvalidPool();
if ((uint160(address(this)) & Hooks.ALL_HOOK_MASK) != REQUIRED_FLAGS) revert InvalidHookAddress();
poolManager = IPoolManager(poolManager_);
positionManager = IPositionManagerSender(positionManager_);
universalRouter = IUniversalRouterSender(universalRouter_);
quoter = quoter_;
gate = IQuiverV4GateAuthorization(gate_);
}
modifier onlyPoolManager() {
if (msg.sender != address(poolManager)) revert OnlyPoolManager();
_;
}
modifier onlyLauncher() {
if (msg.sender != launcher) revert OnlyLauncher();
_;
}
function setLauncher(address launcher_) external onlyOwner {
if (launcher != address(0)) revert LauncherAlreadySet();
if (launcher_ == address(0)) revert InvalidPool();
launcher = launcher_;
emit LauncherSet(launcher_);
}
function registerPool(
PoolKey calldata key,
address token,
address creator,
uint160 initialSqrtPriceX96,
bool gated,
bool liquidityExclusive
) external onlyLauncher returns (PoolId poolId) {
if (
address(key.hooks) != address(this) || token == address(0) || creator == address(0)
|| initialSqrtPriceX96 == 0
) revert InvalidPool();
poolId = key.toId();
if (poolConfigs[poolId].registered) revert InvalidPool();
poolConfigs[poolId] = PoolConfig({
token: token,
creator: creator,
initialSqrtPriceX96: initialSqrtPriceX96,
registered: true,
initialized: false,
gated: gated,
liquidityExclusive: liquidityExclusive,
bootstrapActive: true
});
emit PoolRegistered(poolId, token, creator, initialSqrtPriceX96, gated, liquidityExclusive);
}
function closeBootstrap(PoolId poolId) external onlyLauncher {
PoolConfig storage config = poolConfigs[poolId];
if (!config.registered || !config.bootstrapActive) revert InvalidPool();
config.bootstrapActive = false;
emit BootstrapClosed(poolId);
}
function endGating(PoolId poolId) external {
PoolConfig storage config = poolConfigs[poolId];
if (msg.sender != config.creator) revert OnlyCreator();
if (!config.gated) revert InvalidPool();
config.gated = false;
gate.lockPoolSigner(poolId);
emit GatingEnded(poolId);
}
function disableLiquidityExclusivity(PoolId poolId) external {
PoolConfig storage config = poolConfigs[poolId];
if (msg.sender != config.creator) revert OnlyCreator();
if (!config.liquidityExclusive) revert InvalidPool();
config.liquidityExclusive = false;
emit LiquidityExclusivityDisabled(poolId);
}
/// @notice Returns current policy state aligned one-for-one with `poolIds`.
/// @dev Complements QuiverV4Launcher.launchesRange for efficient recovery.
function poolConfigsBatch(PoolId[] calldata poolIds) external view returns (PoolConfig[] memory configs) {
uint256 count = poolIds.length;
if (count > MAX_VIEW_PAGE) revert PageTooLarge();
configs = new PoolConfig[](count);
for (uint256 index; index < count; ++index) {
configs[index] = poolConfigs[poolIds[index]];
}
}
function beforeInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96)
external
onlyPoolManager
returns (bytes4)
{
PoolConfig storage config = poolConfigs[key.toId()];
if (
!config.registered || config.initialized || sender != launcher || sqrtPriceX96 != config.initialSqrtPriceX96
) revert InvalidInitialization();
config.initialized = true;
return IHooks.beforeInitialize.selector;
}
function beforeAddLiquidity(address sender, PoolKey calldata key, ModifyLiquidityParams calldata, bytes calldata)
external
onlyPoolManager
returns (bytes4)
{
PoolConfig storage config = poolConfigs[key.toId()];
if (!config.registered) revert InvalidPool();
if (config.liquidityExclusive) {
if (
!config.bootstrapActive || sender != address(positionManager) || positionManager.msgSender() != launcher
) revert LiquidityNotAuthorized();
}
return IHooks.beforeAddLiquidity.selector;
}
function beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata hookData)
external
onlyPoolManager
returns (bytes4, BeforeSwapDelta, uint24)
{
PoolId poolId = key.toId();
PoolConfig storage config = poolConfigs[poolId];
if (!config.registered) revert InvalidPool();
Currency output = params.zeroForOne ? key.currency1 : key.currency0;
if (config.gated && Currency.unwrap(output) == config.token && !config.bootstrapActive) {
// The canonical V4 Quoter always reverts its simulation with the
// quote payload, so this path cannot settle a swap or move funds.
if (sender == quoter) {
return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);
}
if (sender != address(universalRouter) || universalRouter.msgSender() != address(gate)) {
revert BuyNotAuthorized();
}
(address buyer, PoolId authorizedPoolId, uint256 nonce) = abi.decode(hookData, (address, PoolId, uint256));
if (PoolId.unwrap(authorizedPoolId) != PoolId.unwrap(poolId) || !gate.isAuthorized(buyer, poolId, nonce)) {
revert BuyNotAuthorized();
}
}
return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);
}
function afterInitialize(address, PoolKey calldata, uint160, int24) external pure returns (bytes4) {
revert UnsupportedHook();
}
function afterAddLiquidity(
address,
PoolKey calldata,
ModifyLiquidityParams calldata,
BalanceDelta,
BalanceDelta,
bytes calldata
) external pure returns (bytes4, BalanceDelta) {
revert UnsupportedHook();
}
function beforeRemoveLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata)
external
pure
returns (bytes4)
{
revert UnsupportedHook();
}
function afterRemoveLiquidity(
address,
PoolKey calldata,
ModifyLiquidityParams calldata,
BalanceDelta,
BalanceDelta,
bytes calldata
) external pure returns (bytes4, BalanceDelta) {
revert UnsupportedHook();
}
function afterSwap(address, PoolKey calldata, SwapParams calldata, BalanceDelta, bytes calldata)
external
pure
returns (bytes4, int128)
{
revert UnsupportedHook();
}
function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external pure returns (bytes4) {
revert UnsupportedHook();
}
function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external pure returns (bytes4) {
revert UnsupportedHook();
}
}
Chain explorer1999msChain node86ms