MemeFolioTaxHook
0x29a2a39143f5aafba61bb8c649bc7c7d50b1e0cc
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
7,863 bytes
ABI entries
44
18 read · 12 write
License
none
Contract information
- Address
- 0x29a2a39143f5aafba61bb8c649bc7c7d50b1e0cc
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x7861A13054…F58333520b
- Creation tx
- 0xb4259e0665…a6fa637c12
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (18)
BPS_DENOMINATOR() → uint256
BUYBACK_BPS() → uint256
POOL_LP_FEE() → uint24
TAX_BPS() → uint256
TREASURY_BPS() → uint256
buybackWallet() → address
calculateTax(uint256) → uint256
getHookPermissions() → tuple
initialSqrtPriceX96() → uint160
launchAuthority() → address
pendingBuybackTax() → uint256
pendingNativeTax() → uint256
pendingTreasuryTax() → uint256
poolManager() → address
poolTickSpacing() → int24
projectToken() → address
splitTax(uint256) → uint256, uint256
treasuryWallet() → address
Events (2)
NativeTaxAccruedNativeTaxForwarded
ABI
[
{
"inputs": [
{
"internalType": "contract IPoolManager",
"name": "poolManager_",
"type": "address"
},
{
"internalType": "address",
"name": "projectToken_",
"type": "address"
},
{
"internalType": "address payable",
"name": "buybackWallet_",
"type": "address"
},
{
"internalType": "address payable",
"name": "treasuryWallet_",
"type": "address"
},
{
"internalType": "int24",
"name": "poolTickSpacing_",
"type": "int24"
},
{
"internalType": "address",
"name": "launchAuthority_",
"type": "address"
},
{
"internalType": "uint160",
"name": "initialSqrtPriceX96_",
"type": "uint160"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "FailedCall",
"type": "error"
},
{
"inputs": [],
"name": "HookNotImplemented",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "InsufficientBalance",
"type": "error"
},
{
"inputs": [],
"name": "InvalidAddress",
"type": "error"
},
{
"inputs": [],
"name": "InvalidPool",
"type": "error"
},
{
"inputs": [],
"name": "InvalidTickSpacing",
"type": "error"
},
{
"inputs": [],
"name": "NotPoolManager",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"inputs": [],
"name": "UnauthorizedInitialization",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "chargedBeforeSwap",
"type": "bool"
}
],
"name": "NativeTaxAccrued",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "buybackWallet",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "buybackAmount",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "treasuryWallet",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "treasuryAmount",
"type": "uint256"
}
],
"name": "NativeTaxForwarded",
"type": "event"
},
{
"inputs": [],
"name": "BPS_DENOMINATOR",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "BUYBACK_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "POOL_LP_FEE",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TAX_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TREASURY_BPS",
"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 ModifyLiquidityParams",
"name": "params",
"type": "tuple"
},
{
"internalType": "BalanceDelta",
"name": "delta0",
"type": "int256"
},
{
"internalType": "BalanceDelta",
"name": "delta1",
"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 ModifyLiquidityParams",
"name": "params",
"type": "tuple"
},
{
"internalType": "BalanceDelta",
"name": "delta0",
"type": "int256"
},
{
"internalType": "BalanceDelta",
"name": "delta1",
"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 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 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 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 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": "buybackWallet",
"outputs": [
{
"internalType": "address payable",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "nativeAmount",
"type": "uint256"
}
],
"name": "calculateTax",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "collectNativeTax",
"outputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"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": "permissions",
"type": "tuple"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "initialSqrtPriceX96",
"outputs": [
{
"internalType": "uint160",
"name": "",
"type": "uint160"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launchAuthority",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingBuybackTax",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingNativeTax",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingTreasuryTax",
"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": [],
"name": "poolTickSpacing",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "projectToken",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "taxAmount",
"type": "uint256"
}
],
"name": "splitTax",
"outputs": [
{
"internalType": "uint256",
"name": "buybackAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "treasuryAmount",
"type": "uint256"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "treasuryWallet",
"outputs": [
{
"internalType": "address payable",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "unlockCallback",
"outputs": [
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import { BaseHook } from "@openzeppelin/uniswap-hooks/base/BaseHook.sol";
import { CurrencySettler } from "@openzeppelin/uniswap-hooks/utils/CurrencySettler.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SignedMath } from "@openzeppelin/contracts/utils/math/SignedMath.sol";
import { IHooks } from "@uniswap/v4-core/src/interfaces/IHooks.sol";
import { IPoolManager } from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import { Hooks } from "@uniswap/v4-core/src/libraries/Hooks.sol";
import { FullMath } from "@uniswap/v4-core/src/libraries/FullMath.sol";
import { SafeCast } from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import { BalanceDelta } from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import { BeforeSwapDelta, toBeforeSwapDelta } from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol";
import { Currency, CurrencyLibrary } from "@uniswap/v4-core/src/types/Currency.sol";
import { PoolKey } from "@uniswap/v4-core/src/types/PoolKey.sol";
import { SwapParams } from "@uniswap/v4-core/src/types/PoolOperation.sol";
/// @title MemeFolioTaxHook
/// @notice Immutable 3% native-ETH swap tax for one exact Meme ETF Uniswap v4 pool.
/// @dev The fee is taken in ETH on both buys and sells, stored as gas-efficient PoolManager claims,
/// and split 2:1 between the buyback and treasury wallets on collection. Redemption is
/// permissionless and both destinations are immutable. No owner, proxy, setter, exemption
/// list, pause or alternate withdrawal path exists.
contract MemeFolioTaxHook is BaseHook, ReentrancyGuard {
using Address for address payable;
using CurrencyLibrary for Currency;
using CurrencySettler for Currency;
using SafeCast for uint256;
uint256 public constant BPS_DENOMINATOR = 10_000;
uint256 public constant TAX_BPS = 300;
uint256 public constant BUYBACK_BPS = 200;
uint256 public constant TREASURY_BPS = 100;
uint24 public constant POOL_LP_FEE = 0;
address public immutable projectToken;
address payable public immutable buybackWallet;
address payable public immutable treasuryWallet;
int24 public immutable poolTickSpacing;
uint160 public immutable initialSqrtPriceX96;
address public launchAuthority;
error InvalidAddress();
error InvalidPool();
error InvalidTickSpacing();
error UnauthorizedInitialization();
event NativeTaxAccrued(uint256 amount, bool chargedBeforeSwap);
event NativeTaxForwarded(
address indexed buybackWallet,
uint256 buybackAmount,
address indexed treasuryWallet,
uint256 treasuryAmount
);
receive() external payable {
if (msg.sender != address(poolManager)) revert NotPoolManager();
}
constructor(
IPoolManager poolManager_,
address projectToken_,
address payable buybackWallet_,
address payable treasuryWallet_,
int24 poolTickSpacing_,
address launchAuthority_,
uint160 initialSqrtPriceX96_
) BaseHook(poolManager_) {
if (
projectToken_ == address(0) || buybackWallet_ == address(0)
|| treasuryWallet_ == address(0) || buybackWallet_ == treasuryWallet_
|| launchAuthority_ == address(0) || initialSqrtPriceX96_ == 0
) revert InvalidAddress();
if (poolTickSpacing_ <= 0) revert InvalidTickSpacing();
projectToken = projectToken_;
buybackWallet = buybackWallet_;
treasuryWallet = treasuryWallet_;
poolTickSpacing = poolTickSpacing_;
launchAuthority = launchAuthority_;
initialSqrtPriceX96 = initialSqrtPriceX96_;
}
function getHookPermissions()
public
pure
override
returns (Hooks.Permissions memory permissions)
{
permissions.beforeInitialize = true;
permissions.beforeSwap = true;
permissions.afterSwap = true;
permissions.beforeSwapReturnDelta = true;
permissions.afterSwapReturnDelta = true;
}
function _beforeInitialize(address sender, PoolKey calldata key, uint160 sqrtPriceX96)
internal
override
returns (bytes4)
{
_validatePool(key);
if (sender != launchAuthority || sqrtPriceX96 != initialSqrtPriceX96) {
revert UnauthorizedInitialization();
}
launchAuthority = address(0);
return IHooks.beforeInitialize.selector;
}
function _beforeSwap(address, PoolKey calldata key, SwapParams calldata params, bytes calldata)
internal
override
returns (bytes4, BeforeSwapDelta, uint24)
{
_validatePool(key);
// The specified currency is ETH for exact-input buys and exact-output sells.
bool nativeIsSpecified = (params.amountSpecified < 0) == params.zeroForOne;
if (!nativeIsSpecified) return (IHooks.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0);
uint256 tax = calculateTax(_absolute(params.amountSpecified));
if (tax == 0) return (IHooks.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0);
key.currency0.take(poolManager, address(this), tax, true);
emit NativeTaxAccrued(tax, true);
return (IHooks.beforeSwap.selector, toBeforeSwapDelta(tax.toInt128(), 0), 0);
}
function _afterSwap(
address,
PoolKey calldata key,
SwapParams calldata params,
BalanceDelta delta,
bytes calldata
) internal override returns (bytes4, int128) {
_validatePool(key);
bool nativeIsSpecified = (params.amountSpecified < 0) == params.zeroForOne;
if (nativeIsSpecified) return (IHooks.afterSwap.selector, 0);
uint256 tax = calculateTax(_absolute(int256(delta.amount0())));
if (tax == 0) return (IHooks.afterSwap.selector, 0);
key.currency0.take(poolManager, address(this), tax, true);
emit NativeTaxAccrued(tax, false);
return (IHooks.afterSwap.selector, tax.toInt128());
}
/// @notice Returns total ETH tax currently claimable by both immutable destinations.
function pendingNativeTax() public view returns (uint256) {
return poolManager.balanceOf(address(this), CurrencyLibrary.ADDRESS_ZERO.toId());
}
function pendingBuybackTax() external view returns (uint256) {
(uint256 buybackAmount,) = splitTax(pendingNativeTax());
return buybackAmount;
}
function pendingTreasuryTax() external view returns (uint256) {
(, uint256 treasuryAmount) = splitTax(pendingNativeTax());
return treasuryAmount;
}
/// @notice Permissionlessly forwards all accrued ETH tax in an immutable 2:1 split.
/// @dev A keeper calls this immediately before the hourly buy-and-distribute cycle.
function collectNativeTax() external nonReentrant returns (uint256 amount) {
amount = pendingNativeTax();
if (amount == 0) return 0;
(uint256 buybackAmount, uint256 treasuryAmount) = splitTax(amount);
poolManager.unlock(abi.encode(amount));
if (buybackAmount != 0) buybackWallet.sendValue(buybackAmount);
if (treasuryAmount != 0) treasuryWallet.sendValue(treasuryAmount);
emit NativeTaxForwarded(
buybackWallet, buybackAmount, treasuryWallet, treasuryAmount
);
}
/// @notice PoolManager callback used only to burn this hook's accrued native-ETH claims.
function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) {
uint256 amount = abi.decode(data, (uint256));
Currency nativeCurrency = CurrencyLibrary.ADDRESS_ZERO;
nativeCurrency.settle(poolManager, address(this), amount, true);
nativeCurrency.take(poolManager, address(this), amount, false);
return "";
}
function calculateTax(uint256 nativeAmount) public pure returns (uint256) {
return FullMath.mulDiv(nativeAmount, TAX_BPS, BPS_DENOMINATOR);
}
/// @notice Splits an already charged 3% tax amount into 2% buyback and 1% treasury shares.
/// @dev The treasury receives the at-most-one-wei division remainder, so no ETH is stranded.
function splitTax(uint256 taxAmount)
public
pure
returns (uint256 buybackAmount, uint256 treasuryAmount)
{
buybackAmount = FullMath.mulDiv(taxAmount, BUYBACK_BPS, TAX_BPS);
treasuryAmount = taxAmount - buybackAmount;
}
function _validatePool(PoolKey calldata key) internal view {
if (
!key.currency0.isAddressZero() || Currency.unwrap(key.currency1) != projectToken
|| key.fee != POOL_LP_FEE || key.tickSpacing != poolTickSpacing
|| address(key.hooks) != address(this)
) revert InvalidPool();
}
function _absolute(int256 amount) private pure returns (uint256) {
return SignedMath.abs(amount);
}
}
Chain explorer2243msChain node79ms