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
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