HoodsHook
0x530e7d4e760163d5d0a33ef20655b1a7af08e0cc
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
8,656 bytes
ABI entries
44
17 read · 17 write
License
none
Contract information
- Address
- 0x530e7d4e760163d5d0a33ef20655b1a7af08e0cc
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x4e59b44847…26c0B4956C
- Creation tx
- 0xf4e6d83442…68a9faf8b9
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (17)
BPS() → uint256
MAX_FEE_BPS() → uint256
TOTAL_SUPPLY() → uint256
deployer() → address
factory() → address
feeFor(uint256) → uint256, uint256
feeTierCount() → uint256
feeTiers(uint256) → uint128, uint16, uint16
getHookPermissions() → tuple
launches(bytes32) → bool, bool, int24
marketCapWei(tuple) → uint256
owner() → address
poolDeploymentBlock(bytes32) → uint256
poolManager() → address
splitter() → address
treasury() → address
treasuryEscrow() → uint256
Events (6)
FeeChargedFeeTiersUpdatedGraduatedLaunchRegisteredOwnerChangedTreasuryChanged
ABI
[
{
"inputs": [
{
"internalType": "contract IPoolManager",
"name": "_poolManager",
"type": "address"
},
{
"internalType": "address",
"name": "treasury_",
"type": "address"
},
{
"internalType": "address",
"name": "splitter_",
"type": "address"
},
{
"internalType": "address",
"name": "wirer_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "HookNotImplemented",
"type": "error"
},
{
"inputs": [],
"name": "NotPoolManager",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "isBuy",
"type": "bool"
},
{
"indexed": false,
"internalType": "uint256",
"name": "feeAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "platformShare",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "creatorShare",
"type": "uint256"
}
],
"name": "FeeCharged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "count",
"type": "uint256"
}
],
"name": "FeeTiersUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "int24",
"name": "tick",
"type": "int24"
}
],
"name": "Graduated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "int24",
"name": "graduationTick",
"type": "int24"
}
],
"name": "LaunchRegistered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnerChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "treasury",
"type": "address"
}
],
"name": "TreasuryChanged",
"type": "event"
},
{
"inputs": [],
"name": "BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_FEE_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TOTAL_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"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": "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": [
{
"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": [],
"name": "deployer",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "mcapWei",
"type": "uint256"
}
],
"name": "feeFor",
"outputs": [
{
"internalType": "uint256",
"name": "totalBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "platformBps",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeTierCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "feeTiers",
"outputs": [
{
"internalType": "uint128",
"name": "mcapThresholdWei",
"type": "uint128"
},
{
"internalType": "uint16",
"name": "totalBps",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "platformBps",
"type": "uint16"
}
],
"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": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "launches",
"outputs": [
{
"internalType": "bool",
"name": "exists",
"type": "bool"
},
{
"internalType": "bool",
"name": "graduated",
"type": "bool"
},
{
"internalType": "int24",
"name": "graduationTick",
"type": "int24"
}
],
"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"
}
],
"name": "marketCapWei",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"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"
}
],
"name": "poke",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "poolDeploymentBlock",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolManager",
"outputs": [
{
"internalType": "contract IPoolManager",
"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": "int24",
"name": "graduationTick",
"type": "int24"
}
],
"name": "registerLaunch",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "factory_",
"type": "address"
}
],
"name": "setFactory",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "uint128",
"name": "mcapThresholdWei",
"type": "uint128"
},
{
"internalType": "uint16",
"name": "totalBps",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "platformBps",
"type": "uint16"
}
],
"internalType": "struct HoodsHook.FeeTier[]",
"name": "tiers",
"type": "tuple[]"
}
],
"name": "setFeeTiers",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "setOwner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "t",
"type": "address"
}
],
"name": "setTreasury",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "splitter",
"outputs": [
{
"internalType": "contract IHoodsSplitter",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "treasury",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "treasuryEscrow",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "withdrawTreasury",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
/*
█ █ ███ ███ ████ ████ █ █ █████ █ █ █████
█ █ █ █ █ █ █ █ █ ██ ██ █ ██ ██ █
█████ █ █ █ █ █ █ ███ █ █ █ ████ █ █ █ ████
█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █
█ █ ███ ███ ████ ████ █ █ █ █████ █ █ █████
The most rewarding place to launch and trade.
Fixed supply · LP burned forever · fees flow to creators.
web https://hoods.meme
x https://x.com/hoodsmeme
tg https://t.me/hoods_meme
*/
import { BaseHook } from "./vendor/BaseHook.sol";
import { Hooks } from "v4-core/src/libraries/Hooks.sol";
import { IPoolManager } from "v4-core/src/interfaces/IPoolManager.sol";
import { PoolKey } from "v4-core/src/types/PoolKey.sol";
import { PoolId } 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, toBeforeSwapDelta } from "v4-core/src/types/BeforeSwapDelta.sol";
import { StateLibrary } from "v4-core/src/libraries/StateLibrary.sol";
import { PoolIdLibrary } from "v4-core/src/types/PoolId.sol";
import { FullMath } from "v4-core/src/libraries/FullMath.sol";
interface IHoodsSplitter {
function credit(address token) external payable;
}
/// @title HoodsHook — singleton fee hook for every hoods.meme pool (arc §2.3)
/// @notice Pool LP fee = 0; this hook takes an ETH fee on every swap, sized by a MARKET-CAP tier curve:
/// high at launch (anti-snipe economics + revenue), decaying at scale so a lower-fee clone pool can't
/// undercut the canonical one. The fee splits platform → treasury, creator → HoodsSplitter (current
/// Creator NFT holder). Launch LIMITS live in HoodsToken (tx.origin-based, non-spoofable), not here.
///
/// Fee-taking follows the verified production pattern (native ETH is always currency0):
/// • ETH is the SPECIFIED currency (buy exactIn) → take in beforeSwap, return +delta on specified.
/// • ETH is the UNSPECIFIED currency (buy exactOut, sell exactIn) → take in afterSwap, return +delta.
/// • sell exactOut is rejected (would need a pre-swap price lookup to size the ETH fee).
///
/// Bounded owner: may retune the tier table (each total ≤ 1%, always), the treasury, and the owner. It
/// CANNOT mint, move LP, seize tokens, redirect the creator share, or halt trading — no such code.
contract HoodsHook is BaseHook {
using StateLibrary for IPoolManager;
using PoolIdLibrary for PoolKey;
uint256 public constant BPS = 10_000;
uint256 public constant MAX_FEE_BPS = 100; // 1% — the hard cap the owner can never exceed
uint256 public constant TOTAL_SUPPLY = 1_000_000_000e18; // 1B (mirrors the factory) — for mcap
uint256 private constant Q96 = 0x1000000000000000000000000; // 2^96
/// Market-cap fee tier. `totalBps` = fee as bps of the ETH swapped; `platformBps` = the platform's
/// slice of that fee (creator gets total − platform). Tiers ascending by threshold; the LAST tier
/// has threshold 0 — the floor that catches every mcap above the previous bound.
struct FeeTier {
uint128 mcapThresholdWei; // upper bound in ETH wei (exclusive); ignored on the last tier
uint16 totalBps;
uint16 platformBps;
}
FeeTier[] public feeTiers;
address public treasury; // platform fee sink (owner-settable)
uint256 public treasuryEscrow; // platform ETH that couldn't be pushed to treasury — pull via withdrawTreasury
IHoodsSplitter public immutable splitter;
address public immutable deployer; // one-shot setFactory authority (hook is CREATE2-deployed, M-1)
address public owner; // bounded admin (see contract notice)
// Launch registry (set once per pool by the factory) — drives the UI/indexer "graduated" flag.
address public factory; // set-once binding (deploy ordering: hook before factory)
struct Launch {
bool exists;
bool graduated; // one-way latch; UI/indexer "graduated"
int24 graduationTick;
}
mapping(bytes32 => Launch) public launches; // poolId => launch data
mapping(bytes32 => uint256) public poolDeploymentBlock; // no fee on the launch block
event LaunchRegistered(bytes32 indexed poolId, address indexed token, int24 graduationTick);
event Graduated(bytes32 indexed poolId, address indexed token, int24 tick);
event FeeCharged(
bytes32 indexed poolId, address indexed token, bool isBuy, uint256 feeAmount, uint256 platformShare, uint256 creatorShare
);
event FeeTiersUpdated(uint256 count);
event TreasuryChanged(address indexed treasury);
event OwnerChanged(address indexed previousOwner, address indexed newOwner);
constructor(IPoolManager _poolManager, address treasury_, address splitter_, address wirer_) BaseHook(_poolManager) {
require(treasury_ != address(0) && splitter_ != address(0) && wirer_ != address(0), "zero");
treasury = treasury_;
splitter = IHoodsSplitter(splitter_);
deployer = wirer_; // the EOA allowed to wire `factory` — hook is CREATE2-deployed so ctor
// msg.sender is the deterministic CREATE2 factory, not the deployer (M-1).
owner = wirer_;
emit OwnerChanged(address(0), wirer_);
_initDefaultFeeTiers();
}
/// Default 6-tier curve. Thresholds ETH-denominated (about $4k/ETH: $2.5M to $25M); total 1% to
/// 0.25%; platform share slides 60% to ~28% as the token grows.
function _initDefaultFeeTiers() private {
feeTiers.push(FeeTier(625e18, 100, 60)); // < ~$2.5M — 1.00%, 60/40
feeTiers.push(FeeTier(1250e18, 85, 46)); // < ~$5M — 0.85%, 54/46
feeTiers.push(FeeTier(2500e18, 70, 34)); // < ~$10M — 0.70%, 49/51
feeTiers.push(FeeTier(4000e18, 55, 23)); // < ~$16M — 0.55%, 42/58
feeTiers.push(FeeTier(6250e18, 40, 14)); // < ~$25M — 0.40%, 35/65
feeTiers.push(FeeTier(0, 25, 7)); // ≥ ~$25M — 0.25%, 28/72 (floor)
}
// ── bounded owner ──
modifier onlyOwner() {
require(msg.sender == owner, "not owner");
_;
}
/// Replace the fee tier table. Each total ≤ 1% (MAX_FEE_BPS) and platform ≤ total — the owner can
/// only ever lower/reshape fees, never gouge. Ascending thresholds; last tier is the floor.
function setFeeTiers(FeeTier[] calldata tiers) external onlyOwner {
require(tiers.length > 0, "empty");
delete feeTiers;
uint256 prev = 0;
for (uint256 i = 0; i < tiers.length; i++) {
require(tiers[i].totalBps <= MAX_FEE_BPS, "fee > 1%");
require(tiers[i].platformBps <= tiers[i].totalBps, "platform > total");
if (i < tiers.length - 1) {
require(tiers[i].mcapThresholdWei > prev, "not ascending");
prev = tiers[i].mcapThresholdWei;
}
feeTiers.push(tiers[i]);
}
emit FeeTiersUpdated(tiers.length);
}
function setTreasury(address t) external onlyOwner {
require(t != address(0), "zero");
treasury = t;
emit TreasuryChanged(t);
}
/// Pull platform ETH that couldn't be pushed at swap time (e.g. treasury temporarily unpayable).
/// Permissionless — funds only ever go to the CURRENT treasury.
function withdrawTreasury() external {
uint256 amount = treasuryEscrow;
require(amount > 0, "nothing");
treasuryEscrow = 0;
(bool ok,) = payable(treasury).call{ value: amount }("");
require(ok, "send failed");
}
function setOwner(address newOwner) external onlyOwner {
emit OwnerChanged(owner, newOwner);
owner = newOwner;
}
function feeTierCount() external view returns (uint256) {
return feeTiers.length;
}
/// One-shot binding of the factory (breaks the hook↔factory deploy cycle). Deployer-only so it can't
/// be front-run into a griefing address before the real factory binds (would brick trading, M-1).
function setFactory(address factory_) external {
require(msg.sender == deployer, "not deployer");
require(factory == address(0) && factory_ != address(0), "factory set");
factory = factory_;
}
/// Factory-only, once per pool: record the graduation latch tick.
function registerLaunch(PoolKey calldata key, int24 graduationTick) external {
require(msg.sender == factory, "not factory");
bytes32 id = PoolId.unwrap(_toId(key));
require(!launches[id].exists, "registered");
launches[id] = Launch({ exists: true, graduated: false, graduationTick: graduationTick });
emit LaunchRegistered(id, _token(key), graduationTick);
}
/// Permissionless: latch graduation from the current tick without needing a swap (keeper/indexer).
function poke(PoolKey calldata key) external {
_updateLatch(key);
}
/// One-way latch: sets graduated once the pool tick has fallen to/through graduationTick (buys move
/// the tick DOWN). Purely informational now — limits live in the token on a time window.
function _updateLatch(PoolKey calldata key) internal {
bytes32 id = PoolId.unwrap(_toId(key));
Launch storage L = launches[id];
if (!L.exists || L.graduated) return;
(, int24 tick,,) = poolManager.getSlot0(key.toId());
if (tick <= L.graduationTick) {
L.graduated = true;
emit Graduated(id, _token(key), tick);
}
}
// ── fee sizing (market-cap tiered) ──
/// Fully-diluted market cap in ETH wei from the current pool price (ETH is currency0). Zero if the
/// pool is uninitialised. mcap = totalSupply × ethPerToken, where ethPerToken = (2^96 / sqrtP)^2.
function marketCapWei(PoolKey calldata key) public view returns (uint256) {
(uint160 sqrtP,,,) = poolManager.getSlot0(key.toId());
if (sqrtP == 0) return 0;
uint256 inv = FullMath.mulDiv(Q96, Q96, sqrtP); // = sqrt(ethPerToken) × 2^96
return FullMath.mulDiv(FullMath.mulDiv(TOTAL_SUPPLY, inv, Q96), inv, Q96);
}
/// (totalBps, platformBps) for a market cap — first tier it's under, else the floor (last tier).
function feeFor(uint256 mcapWei) public view returns (uint256 totalBps, uint256 platformBps) {
uint256 n = feeTiers.length;
for (uint256 i = 0; i < n; i++) {
if (i == n - 1 || mcapWei < feeTiers[i].mcapThresholdWei) {
return (feeTiers[i].totalBps, feeTiers[i].platformBps);
}
}
return (0, 0); // unreachable while tiers are non-empty
}
function _feeBps(PoolKey calldata key) private view returns (uint256 totalBps, uint256 platformBps) {
return feeFor(marketCapWei(key));
}
// ── permissions ──
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: true,
afterInitialize: false,
beforeAddLiquidity: false,
afterAddLiquidity: false,
beforeRemoveLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true,
afterSwap: true,
beforeDonate: false,
afterDonate: false,
beforeSwapReturnDelta: true,
afterSwapReturnDelta: true,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
function _beforeInitialize(address, PoolKey calldata key, uint160) internal override returns (bytes4) {
poolDeploymentBlock[PoolId.unwrap(_toId(key))] = block.number;
return BaseHook.beforeInitialize.selector;
}
// ─────────────────────────────── fee taking ───────────────────────────────
/// ETH-specified path: buy exactIn (fee peeled from the ETH input). Sell exactOut is rejected.
function _beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)
internal
override
returns (bytes4, BeforeSwapDelta, uint24)
{
address token = _token(key);
if (token == address(0)) return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0);
// Only charge fees on factory-registered pools. A rogue ETH/X pool that attaches this hook would
// otherwise accrue creator `pending` for a token with no Creator NFT → stranded forever (M-3).
if (!launches[PoolId.unwrap(_toId(key))].exists) return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0);
_updateLatch(key); // one-way graduation check from the pre-swap tick
bool isBuy = _isBuy(key, params);
bool isExactInput = params.amountSpecified < 0;
// sell exactOut: ETH is specified but sizing the fee needs a pre-swap price lookup → reject.
require(isBuy || isExactInput, "exactOutput sells unsupported");
// handled in afterSwap when ETH is the unspecified side (buy exactOut, sell exactIn)
if (!(isBuy && isExactInput)) return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0);
uint256 ethIn = uint256(-params.amountSpecified);
if (_noFee(key)) return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0);
(uint256 totalBps, uint256 platformBps) = _feeBps(key);
uint256 feeAmount = (ethIn * totalBps) / BPS;
if (feeAmount == 0) return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0);
poolManager.take(key.currency0, address(this), feeAmount); // currency0 == ETH
_distribute(key, token, feeAmount, true, platformBps, totalBps);
return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(int128(int256(feeAmount)), 0), 0);
}
/// ETH-unspecified path: buy exactOut or sell exactIn (fee peeled from the ETH leg of the delta).
function _afterSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, BalanceDelta delta, bytes calldata)
internal
override
returns (bytes4, int128)
{
address token = _token(key);
if (token == address(0)) return (BaseHook.afterSwap.selector, 0);
if (!launches[PoolId.unwrap(_toId(key))].exists) return (BaseHook.afterSwap.selector, 0); // unregistered pool — no fee (M-3)
// Latch graduation from the POST-swap tick so the crossing trade graduates instantly.
// (_beforeSwap latches from the pre-swap tick, which is one trade late for the crosser;
// cheap once latched — _updateLatch early-returns on the one-way flag.)
_updateLatch(key);
bool isBuy = _isBuy(key, params);
// ETH is specified iff (isBuy == isExactInput); the fee for those was already taken in beforeSwap.
if (isBuy == (params.amountSpecified < 0)) return (BaseHook.afterSwap.selector, 0);
int128 ethDelta = delta.amount0(); // currency0 == ETH
uint256 ethMoved = ethDelta < 0 ? uint256(int256(-ethDelta)) : uint256(int256(ethDelta));
if (_noFee(key)) return (BaseHook.afterSwap.selector, 0);
(uint256 totalBps, uint256 platformBps) = _feeBps(key);
uint256 feeAmount = (ethMoved * totalBps) / BPS;
if (feeAmount == 0) return (BaseHook.afterSwap.selector, 0);
poolManager.take(key.currency0, address(this), feeAmount);
_distribute(key, token, feeAmount, false, platformBps, totalBps);
return (BaseHook.afterSwap.selector, int128(int256(feeAmount)));
}
// ── internals ──
function _distribute(PoolKey calldata key, address token, uint256 feeAmount, bool isBuy, uint256 platformBps, uint256 totalBps)
private
{
uint256 platformShare = totalBps == 0 ? 0 : (feeAmount * platformBps) / totalBps;
uint256 creatorShare = feeAmount - platformShare;
if (platformShare > 0) {
// NEVER revert the swap on a bad/unpayable treasury (audit H-1): a reverting treasury would
// brick ALL trading. Escrow-on-failure; pull via withdrawTreasury().
(bool ok,) = payable(treasury).call{ value: platformShare }("");
if (!ok) treasuryEscrow += platformShare;
}
if (creatorShare > 0) {
splitter.credit{ value: creatorShare }(token);
}
emit FeeCharged(PoolId.unwrap(_toId(key)), token, isBuy, feeAmount, platformShare, creatorShare);
}
/// No fee on the pool's launch block (the atomic dev-buy is free).
function _noFee(PoolKey calldata key) private view returns (bool) {
return block.number == poolDeploymentBlock[PoolId.unwrap(_toId(key))];
}
/// Token is the non-ETH currency; our pools always have currency0 == native ETH.
function _token(PoolKey calldata key) private pure returns (address) {
if (Currency.unwrap(key.currency0) == address(0)) return Currency.unwrap(key.currency1);
if (Currency.unwrap(key.currency1) == address(0)) return Currency.unwrap(key.currency0);
return address(0);
}
function _isBuy(PoolKey calldata key, IPoolManager.SwapParams calldata params) private pure returns (bool) {
// buy = acquiring the token with ETH. ETH is currency0 ⇒ zeroForOne is a buy.
return Currency.unwrap(key.currency0) == address(0) ? params.zeroForOne : !params.zeroForOne;
}
function _toId(PoolKey calldata key) private pure returns (PoolId) {
return PoolId.wrap(keccak256(abi.encode(key)));
}
receive() external payable { }
}
Chain explorer2840msChain node100ms