ProtectedCurveHookV2
0x82abbde617e76f0714ede5fb59c3667c9826a888
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
19,902 bytes
ABI entries
59
31 read · 6 write
License
none
Contract information
- Address
- 0x82abbde617e76f0714ede5fb59c3667c9826a888
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x4e59b44847…26c0B4956C
- Creation tx
- 0xeedecd1207…c25e3b34b1
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (31)
BETA_MAX() → uint256
BETA_MIN() → uint256
BUY_PLATFORM_BPS() → uint256
BUY_VAULT_BPS() → uint256
DEFAULT_BETA() → uint256
GENESIS_TOKENS_PER_ETH() → uint256
MIN_SEED() → uint256
SELL_CREATOR_BPS() → uint256
SELL_VAULT_KEEP_BPS() → uint256
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
beforeAddLiquidity(address, tuple, tuple, bytes) → bytes4
beforeDonate(address, tuple, uint256, uint256, bytes) → bytes4
beforeInitialize(address, tuple, uint160) → bytes4
beforeRemoveLiquidity(address, tuple, tuple, bytes) → bytes4
creatorFees(address) → uint256
curveState(bytes32) → address, address, uint256, uint256, uint256, uint256, uint256, uint256
curves(bytes32) → address, uint64, uint32, address, uint128, uint128, uint128
feePayeeA() → address
feePayeeB() → address
networkPoolId() → bytes32
networkSet() → bool
owner() → address
pendingBurnFees() → uint256
poolManager() → address
poolOf(address) → bytes32
quoteBuy(bytes32, uint256) → uint256
quoteSell(bytes32, uint256) → uint256
Events (5)
BuyAndBurnEventCreateEventCreatorFeesClaimedNetworkPoolSetTradeEvent
ABI
[
{
"inputs": [
{
"internalType": "contract IPoolManager",
"name": "_pm",
"type": "address"
},
{
"internalType": "address",
"name": "_owner",
"type": "address"
},
{
"internalType": "address",
"name": "_a",
"type": "address"
},
{
"internalType": "address",
"name": "_b",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AmountOverflow",
"type": "error"
},
{
"inputs": [],
"name": "BetaOutOfRange",
"type": "error"
},
{
"inputs": [],
"name": "DustTrade",
"type": "error"
},
{
"inputs": [],
"name": "ExactOutputNotSupported",
"type": "error"
},
{
"inputs": [],
"name": "FloorViolated",
"type": "error"
},
{
"inputs": [],
"name": "HookNotCallable",
"type": "error"
},
{
"inputs": [],
"name": "LiquidityDisabled",
"type": "error"
},
{
"inputs": [],
"name": "NetworkAlreadySet",
"type": "error"
},
{
"inputs": [],
"name": "NotLaunchpad",
"type": "error"
},
{
"inputs": [],
"name": "OnlyOwner",
"type": "error"
},
{
"inputs": [],
"name": "OnlyPoolManager",
"type": "error"
},
{
"inputs": [],
"name": "SeedTooSmall",
"type": "error"
},
{
"inputs": [],
"name": "SellExceedsSupply",
"type": "error"
},
{
"inputs": [],
"name": "SupplyCapExceeded",
"type": "error"
},
{
"inputs": [],
"name": "UnknownCurve",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokenNotional",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "networkVault",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "networkSupply",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "BuyAndBurnEvent",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "mint",
"type": "address"
},
{
"indexed": true,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "user",
"type": "address"
},
{
"indexed": false,
"internalType": "string",
"name": "name",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "uri",
"type": "string"
},
{
"indexed": false,
"internalType": "uint256",
"name": "beta",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "vault",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "supply",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "CreateEvent",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "CreatorFeesClaimed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint256",
"name": "sweptFees",
"type": "uint256"
}
],
"name": "NetworkPoolSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "mint",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "user",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "isBuy",
"type": "bool"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "vault",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "supply",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "nav",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "priceWad",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "TradeEvent",
"type": "event"
},
{
"inputs": [],
"name": "BETA_MAX",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "BETA_MIN",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "BUY_PLATFORM_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "BUY_VAULT_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "DEFAULT_BETA",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "GENESIS_TOKENS_PER_ETH",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MIN_SEED",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "SELL_CREATOR_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "SELL_VAULT_KEEP_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"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": "",
"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": "beforeAddLiquidity",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "view",
"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": "",
"type": "tuple"
},
{
"internalType": "uint160",
"name": "",
"type": "uint160"
}
],
"name": "beforeInitialize",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "view",
"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": "address",
"name": "",
"type": "address"
}
],
"name": "creatorFees",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "PoolId",
"name": "id",
"type": "bytes32"
}
],
"name": "curveState",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint256",
"name": "vault",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "supply",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "beta",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "navWad",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "priceWad",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maxLossBps",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "PoolId",
"name": "",
"type": "bytes32"
}
],
"name": "curves",
"outputs": [
{
"internalType": "contract CurveToken",
"name": "token",
"type": "address"
},
{
"internalType": "uint64",
"name": "beta",
"type": "uint64"
},
{
"internalType": "uint32",
"name": "createdAt",
"type": "uint32"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "uint128",
"name": "vault",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "supply",
"type": "uint128"
},
{
"internalType": "uint128",
"name": "tokenReserve",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feePayeeA",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feePayeeB",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "uri",
"type": "string"
},
{
"internalType": "uint256",
"name": "betaWad",
"type": "uint256"
}
],
"name": "launch",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "networkPoolId",
"outputs": [
{
"internalType": "PoolId",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "networkSet",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingBurnFees",
"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": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "poolOf",
"outputs": [
{
"internalType": "PoolId",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "PoolId",
"name": "id",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "ethIn",
"type": "uint256"
}
],
"name": "quoteBuy",
"outputs": [
{
"internalType": "uint256",
"name": "tokensOut",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "PoolId",
"name": "id",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "tokensIn",
"type": "uint256"
}
],
"name": "quoteSell",
"outputs": [
{
"internalType": "uint256",
"name": "ethOut",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
}
],
"name": "setNetworkPool",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "unlockCallback",
"outputs": [
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "withdrawCreatorFees",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {IUnlockCallback} from "v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {IHooks} from "v4-core/src/interfaces/IHooks.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 {ModifyLiquidityParams, SwapParams} from "v4-core/src/types/PoolOperation.sol";
import {BeforeSwapDelta, toBeforeSwapDelta} from "v4-core/src/types/BeforeSwapDelta.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {CurrencySettler} from "./CurrencySettler.sol";
import {CurveToken} from "./CurveToken.sol";
/// @title ProtectedCurveHookV2
/// @notice The protected curve, re-architected for STANDARD v4 settle-after
/// accounting so a stock `V4Quoter` (and any standard router) can simulate and
/// route it — the prerequisite for app.uniswap.org / aggregator listing.
///
/// The curve math, fees, and invariants are identical to v1. The ONLY change is
/// custody: instead of taking real ETH into the hook mid-swap (which needed a
/// settle-before-swap router and made the pool un-quotable), the vault ETH lives
/// as ERC-6909 CLAIMS inside the PoolManager, and every `beforeSwap` moves value
/// purely through claims + deltas — no real balance is required during a quote.
///
/// - vault ETH -> 6909 claim of currency0 held by this hook
/// - token side -> 6909 claim reserve (buyback inventory) + elastic mint
/// - fees (ETH) -> pull ledger; withdrawn by redeeming ETH claims via unlock
contract ProtectedCurveHookV2 is IHooks, IUnlockCallback {
using FixedPointMathLib for uint256;
using CurrencySettler for Currency;
// ---------------------------------------------------------------- state
struct Curve {
CurveToken token; // slot0: 20 bytes
uint64 beta; // 8 bytes (WAD)
uint32 createdAt; // 4 bytes
address creator; // slot1: 20 bytes
uint128 vault; // slot2: 16 bytes (wei ETH backing, held as 6909)
uint128 supply; // 16 bytes (internal circulating supply)
uint128 tokenReserve; // slot3: 16 bytes (6909 buyback inventory)
}
IPoolManager public immutable poolManager;
address public owner;
address public immutable feePayeeA;
address public immutable feePayeeB;
mapping(PoolId => Curve) public curves;
mapping(address => PoolId) public poolOf;
PoolId public networkPoolId;
bool public networkSet;
uint256 public pendingBurnFees;
mapping(address => uint256) public creatorFees;
// ------------------------------------------------------------ constants
uint256 internal constant WAD = 1e18;
uint256 internal constant BPS = 10_000;
uint256 public constant BETA_MIN = 0.825e18;
uint256 public constant BETA_MAX = 0.99e18;
uint256 public constant DEFAULT_BETA = 0.9e18;
uint256 public constant BUY_PLATFORM_BPS = 100;
uint256 public constant BUY_VAULT_BPS = 200;
uint256 public constant SELL_CREATOR_BPS = 100;
uint256 public constant SELL_VAULT_KEEP_BPS = 500;
uint256 public constant MIN_SEED = 0.0001 ether;
uint256 public constant GENESIS_TOKENS_PER_ETH = 1_000_000;
uint256 internal constant MAX_SUPPLY = 1e36;
uint160 internal constant SQRT_PRICE_1_1 = 79228162514264337593543950336;
// unlock actions
uint8 internal constant ACT_DEPOSIT = 1;
uint8 internal constant ACT_WITHDRAW = 2;
// --------------------------------------------------------------- events
event CreateEvent(
address indexed mint, PoolId indexed poolId, address indexed user,
string name, string symbol, string uri,
uint256 beta, uint256 vault, uint256 supply, uint256 timestamp
);
event TradeEvent(
address indexed mint, address indexed user, bool isBuy,
uint256 ethAmount, uint256 tokenAmount, uint256 vault, uint256 supply,
uint256 nav, uint256 priceWad, uint256 timestamp
);
event BuyAndBurnEvent(
uint256 ethAmount, uint256 tokenNotional, uint256 networkVault, uint256 networkSupply, uint256 timestamp
);
event NetworkPoolSet(PoolId indexed poolId, uint256 sweptFees);
event CreatorFeesClaimed(address indexed creator, uint256 amount);
// --------------------------------------------------------------- errors
error OnlyPoolManager();
error OnlyOwner();
error NotLaunchpad();
error LiquidityDisabled();
error HookNotCallable();
error UnknownCurve();
error ExactOutputNotSupported();
error AmountOverflow();
error DustTrade();
error SellExceedsSupply();
error FloorViolated();
error BetaOutOfRange();
error SeedTooSmall();
error NetworkAlreadySet();
error SupplyCapExceeded();
constructor(IPoolManager _pm, address _owner, address _a, address _b) {
poolManager = _pm;
owner = _owner;
feePayeeA = _a;
feePayeeB = _b;
}
receive() external payable {}
modifier onlyPoolManager() {
if (msg.sender != address(poolManager)) revert OnlyPoolManager();
_;
}
modifier onlyOwner() {
if (msg.sender != owner) revert OnlyOwner();
_;
}
// --------------------------------------------------------------- launch
function launch(string calldata name, string calldata symbol, string calldata uri, uint256 betaWad)
external
payable
returns (address token, PoolId poolId)
{
if (msg.value < MIN_SEED) revert SeedTooSmall();
uint256 beta = betaWad == 0 ? DEFAULT_BETA : betaWad;
if (beta < BETA_MIN || beta > BETA_MAX) revert BetaOutOfRange();
CurveToken t = new CurveToken(name, symbol, uri);
token = address(t);
PoolKey memory key = PoolKey({
currency0: Currency.wrap(address(0)),
currency1: Currency.wrap(token),
fee: 0,
tickSpacing: 60,
hooks: IHooks(address(this))
});
poolId = key.toId();
poolManager.initialize(key, SQRT_PRICE_1_1);
uint256 s0 = msg.value * GENESIS_TOKENS_PER_ETH;
t.mint(msg.sender, s0); // genesis tokens to the creator (real, circulating)
curves[poolId] = Curve({
token: t,
beta: uint64(beta),
createdAt: uint32(block.timestamp),
creator: msg.sender,
vault: uint128(msg.value),
supply: uint128(s0),
tokenReserve: 0
});
poolOf[token] = poolId;
// Deposit the seed ETH into the PoolManager as a 6909 claim (the vault).
poolManager.unlock(abi.encode(ACT_DEPOSIT, msg.value, address(0)));
emit CreateEvent(token, poolId, msg.sender, name, symbol, uri, beta, msg.value, s0, block.timestamp);
emit TradeEvent(
token, msg.sender, true, msg.value, s0, msg.value, s0,
_navWad(msg.value, s0), _priceWad(msg.value, s0, beta), block.timestamp
);
}
function setNetworkPool(PoolId poolId) external onlyOwner {
if (networkSet) revert NetworkAlreadySet();
if (address(curves[poolId].token) == address(0)) revert UnknownCurve();
networkPoolId = poolId;
networkSet = true;
uint256 pending = pendingBurnFees;
pendingBurnFees = 0;
if (pending > 0) _buyAndBurn(pending);
emit NetworkPoolSet(poolId, pending);
}
/// @notice Withdraw accrued ETH fees. Redeems the hook's ETH 6909 claims to
/// real ETH via an unlock and forwards to the caller.
function withdrawCreatorFees() external {
uint256 amount = creatorFees[msg.sender];
if (amount == 0) return;
creatorFees[msg.sender] = 0;
poolManager.unlock(abi.encode(ACT_WITHDRAW, amount, msg.sender));
emit CreatorFeesClaimed(msg.sender, amount);
}
function transferOwnership(address newOwner) external onlyOwner {
owner = newOwner;
}
// --------------------------------------------------------- unlock callback
function unlockCallback(bytes calldata data) external override onlyPoolManager returns (bytes memory) {
(uint8 action, uint256 amount, address who) = abi.decode(data, (uint8, uint256, address));
Currency eth = Currency.wrap(address(0));
if (action == ACT_DEPOSIT) {
// pay `amount` real ETH into the PM and receive an equal 6909 claim
eth.settle(poolManager, address(this), amount, false);
eth.take(poolManager, address(this), amount, true);
} else if (action == ACT_WITHDRAW) {
// burn `amount` of our ETH 6909 claim and send real ETH to `who`
eth.settle(poolManager, address(this), amount, true);
eth.take(poolManager, who, amount, false);
}
return "";
}
// ---------------------------------------------------------- hook: swaps
function beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata hookData)
external
onlyPoolManager
returns (bytes4, BeforeSwapDelta, uint24)
{
PoolId id = key.toId();
Curve storage c = curves[id];
if (address(c.token) == address(0)) revert UnknownCurve();
if (params.amountSpecified >= 0) revert ExactOutputNotSupported();
uint256 amtIn = uint256(-params.amountSpecified);
if (amtIn > uint256(uint128(type(int128).max))) revert AmountOverflow();
address trader = hookData.length >= 32 ? abi.decode(hookData, (address)) : sender;
uint256 amtOut = params.zeroForOne
? _buy(c, key, amtIn, trader)
: _sell(c, key, amtIn, trader);
if (amtOut > uint256(uint128(type(int128).max))) revert AmountOverflow();
// specified = +amtIn (hook consumed the input), unspecified = -amtOut (hook owes output)
return (
IHooks.beforeSwap.selector,
toBeforeSwapDelta(int128(uint128(amtIn)), -int128(uint128(amtOut))),
0
);
}
function _buy(Curve storage c, PoolKey calldata key, uint256 ethIn, address trader)
internal
returns (uint256 minted)
{
uint256 platformFee = ethIn * BUY_PLATFORM_BPS / BPS;
uint256 backing = ethIn * (BPS - BUY_PLATFORM_BPS - BUY_VAULT_BPS) / BPS;
uint256 vaultCredit = ethIn - platformFee;
uint256 v = c.vault;
uint256 s = c.supply;
uint256 ratioWad = (v + backing).mulDiv(WAD, v);
uint256 growWad = uint256(FixedPointMathLib.powWad(int256(ratioWad), int256(uint256(c.beta))));
uint256 newSupply = s.mulWad(growWad);
if (newSupply > MAX_SUPPLY) revert SupplyCapExceeded();
minted = newSupply > s ? newSupply - s : 0;
if (minted == 0) revert DustTrade();
if (minted * v > vaultCredit * s) revert FloorViolated();
uint256 newVault = v + vaultCredit;
if (newVault > type(uint128).max) revert AmountOverflow();
c.vault = uint128(newVault);
c.supply = uint128(s + minted);
_routePlatformFee(platformFee, c.creator);
// Input ETH -> 6909 claim to the hook (the vault). Simulatable.
key.currency0.take(poolManager, address(this), ethIn, true);
// Deliver `minted` tokens: from the 6909 buyback reserve first, mint the rest.
uint256 fromReserve = c.tokenReserve >= minted ? minted : c.tokenReserve;
if (fromReserve > 0) {
c.tokenReserve = uint128(c.tokenReserve - fromReserve);
key.currency1.settle(poolManager, address(this), fromReserve, true); // burn 6909 to pay
}
uint256 newMint = minted - fromReserve;
if (newMint > 0) {
c.token.mint(address(this), newMint);
key.currency1.settle(poolManager, address(this), newMint, false); // transfer real to pay
}
emit TradeEvent(
address(c.token), trader, true, ethIn, minted, newVault, s + minted,
_navWad(newVault, s + minted), _priceWad(newVault, s + minted, c.beta), block.timestamp
);
}
function _sell(Curve storage c, PoolKey calldata key, uint256 tokensIn, address trader)
internal
returns (uint256 payout)
{
uint256 v = c.vault;
uint256 s = c.supply;
if (tokensIn >= s) revert SellExceedsSupply();
uint256 gross = tokensIn.mulDiv(v, s);
uint256 creatorFee = gross * SELL_CREATOR_BPS / BPS;
payout = gross * (BPS - SELL_CREATOR_BPS - SELL_VAULT_KEEP_BPS) / BPS;
if (payout == 0) revert DustTrade();
uint256 newVault = v - payout - creatorFee;
uint256 newSupply = s - tokensIn;
if (newVault * s < v * newSupply) revert FloorViolated();
c.vault = uint128(newVault);
c.supply = uint128(newSupply);
c.tokenReserve = uint128(c.tokenReserve + tokensIn);
creatorFees[c.creator] += creatorFee;
// Input tokens -> 6909 claim reserve. Output ETH -> burn our 6909 ETH claim to pay.
key.currency1.take(poolManager, address(this), tokensIn, true);
key.currency0.settle(poolManager, address(this), payout, true);
emit TradeEvent(
address(c.token), trader, false, payout, tokensIn, newVault, newSupply,
_navWad(newVault, newSupply), _priceWad(newVault, newSupply, c.beta), block.timestamp
);
}
function _routePlatformFee(uint256 platformFee, address creator) internal {
uint256 burnShare = platformFee / 4;
uint256 quarterA = platformFee / 4;
uint256 quarterB = platformFee / 4;
uint256 creatorShare = platformFee - burnShare - quarterA - quarterB;
creatorFees[feePayeeA] += quarterA;
creatorFees[feePayeeB] += quarterB;
creatorFees[creator] += creatorShare;
if (burnShare > 0) {
if (networkSet) _buyAndBurn(burnShare);
else pendingBurnFees += burnShare;
}
}
function _buyAndBurn(uint256 ethFee) internal {
Curve storage n = curves[networkPoolId];
uint256 v = n.vault;
uint256 s = n.supply;
uint256 ratioWad = (v + ethFee).mulDiv(WAD, v);
uint256 growWad = uint256(FixedPointMathLib.powWad(int256(ratioWad), int256(uint256(n.beta))));
uint256 notional = s.mulWad(growWad);
notional = notional > s ? notional - s : 0;
uint256 newVault = v + ethFee;
if (newVault > type(uint128).max) revert AmountOverflow();
n.vault = uint128(newVault);
emit BuyAndBurnEvent(ethFee, notional, newVault, s, block.timestamp);
}
// ------------------------------------------------------- hook: guardrails
function beforeInitialize(address sender, PoolKey calldata, uint160) external view onlyPoolManager returns (bytes4) {
if (sender != address(this)) revert NotLaunchpad();
return IHooks.beforeInitialize.selector;
}
function beforeAddLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata)
external view onlyPoolManager returns (bytes4)
{
revert LiquidityDisabled();
}
function afterInitialize(address, PoolKey calldata, uint160, int24) external pure returns (bytes4) { revert HookNotCallable(); }
function afterAddLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata) external pure returns (bytes4, BalanceDelta) { revert HookNotCallable(); }
function beforeRemoveLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata) external pure returns (bytes4) { revert HookNotCallable(); }
function afterRemoveLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, BalanceDelta, BalanceDelta, bytes calldata) external pure returns (bytes4, BalanceDelta) { revert HookNotCallable(); }
function afterSwap(address, PoolKey calldata, SwapParams calldata, BalanceDelta, bytes calldata) external pure returns (bytes4, int128) { revert HookNotCallable(); }
function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external pure returns (bytes4) { revert HookNotCallable(); }
function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata) external pure returns (bytes4) { revert HookNotCallable(); }
// ---------------------------------------------------------------- views
function quoteBuy(PoolId id, uint256 ethIn) external view returns (uint256 tokensOut) {
Curve storage c = curves[id];
if (address(c.token) == address(0)) revert UnknownCurve();
uint256 backing = ethIn * (BPS - BUY_PLATFORM_BPS - BUY_VAULT_BPS) / BPS;
uint256 v = c.vault;
uint256 s = c.supply;
uint256 ratioWad = (v + backing).mulDiv(WAD, v);
uint256 growWad = uint256(FixedPointMathLib.powWad(int256(ratioWad), int256(uint256(c.beta))));
uint256 newSupply = s.mulWad(growWad);
tokensOut = newSupply > s ? newSupply - s : 0;
}
function quoteSell(PoolId id, uint256 tokensIn) external view returns (uint256 ethOut) {
Curve storage c = curves[id];
if (address(c.token) == address(0)) revert UnknownCurve();
uint256 s = c.supply;
if (tokensIn >= s) revert SellExceedsSupply();
uint256 gross = tokensIn.mulDiv(uint256(c.vault), s);
ethOut = gross * (BPS - SELL_CREATOR_BPS - SELL_VAULT_KEEP_BPS) / BPS;
}
function curveState(PoolId id)
external view
returns (address token, address creator, uint256 vault, uint256 supply, uint256 beta, uint256 navWad, uint256 priceWad, uint256 maxLossBps)
{
Curve storage c = curves[id];
if (address(c.token) == address(0)) revert UnknownCurve();
token = address(c.token);
creator = c.creator;
vault = c.vault;
supply = c.supply;
beta = c.beta;
navWad = _navWad(vault, supply);
priceWad = _priceWad(vault, supply, beta);
maxLossBps = BPS - (uint256(9118) * beta / WAD);
}
function _navWad(uint256 v, uint256 s) internal pure returns (uint256) {
return s == 0 ? 0 : v.mulDiv(WAD, s);
}
function _priceWad(uint256 v, uint256 s, uint256 beta) internal pure returns (uint256) {
return s == 0 ? 0 : v.mulDiv(WAD, s).mulDiv(WAD, beta);
}
}
Chain explorer8332msChain node97ms