SazareHookV2

0xbbe4baf7d38b7465beddffeaf29f96fc50c8a888

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
13,391 bytes
ABI entries
66
44 read · 4 write
License
none

Contract information

Address
0xbbe4baf7d38b7465beddffeaf29f96fc50c8a888
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xefd818cfc0…a22ac3ffff

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (44)

BAND_BUY_SHARE_BPS()uint256
BPS()uint256
CAP()uint256
DEAD()address
FLOOR_BUY_SHARE_BPS()uint256
FLOOR_FEE_SHARE_BPS()uint256
SWAP_FEE_BPS()uint256
TICK_SPACING()int24
WAD()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
bandReserve()uint256
bandState()tuple
beforeAddLiquidity(address, tuple, tuple, bytes)bytes4
beforeDonate(address, tuple, uint256, uint256, bytes)bytes4
beforeRemoveLiquidity(address, tuple, tuple, bytes)bytes4
burnedForever()uint256
curveMath()address
curveScale()uint256
feeRecipient()address
floorPrice()uint256
floorReserve()uint256
initialized()bool
inventory()uint256
inventoryBacking()uint256
issuancePrice()uint256
issuanceScale()uint256
mintedEver()uint256
pInit()uint256
poolManager()address
quoteBuy(uint256)uint256, uint256, uint256
quoteBuyExactTokens(uint256)uint256, uint256, uint256
redeemableSupply()uint256
saleCap()uint256
spotPrice()uint256
tailInventory()uint256
token()address
totalDepthContributed()uint256
totalInventoryContributed()uint256
totalProtocolFeesPaid()uint256
weth()address

Events (4)

DepthAddedInventoryDepthAddedProtocolFeePaidSazareSwap

ABI

[
  {
    "inputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "poolManager_",
        "type": "address"
      },
      {
        "internalType": "contract ERC20",
        "name": "weth_",
        "type": "address"
      },
      {
        "internalType": "contract SazareV2Math",
        "name": "curveMath_",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "cap_",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "pInit_",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "curveScale_",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "tailInventory_",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "reserveSeed",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "dust",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "reserveSeeder",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "feeRecipient_",
        "type": "address"
      },
      {
        "internalType": "string",
        "name": "tokenName_",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "tokenSymbol_",
        "type": "string"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AlreadyInitialized",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "AmountTooLarge",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "CapExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ExactOutput",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientBandReserve",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientInventory",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidParameter",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPool",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LiquidityForbidden",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotPostCap",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyPoolManager",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SlippageExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroOutput",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "contributor",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "wethAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "scaleAdded",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newScale",
        "type": "uint256"
      }
    ],
    "name": "DepthAdded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "contributor",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "newInventory",
        "type": "uint256"
      }
    ],
    "name": "InventoryDepthAdded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "ProtocolFeePaid",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bool",
        "name": "isBuy",
        "type": "bool"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amountIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amountOut",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "spotPriceAfter",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "floorPriceAfter",
        "type": "uint256"
      }
    ],
    "name": "SazareSwap",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "BAND_BUY_SHARE_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "CAP",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "DEAD",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "FLOOR_BUY_SHARE_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "FLOOR_FEE_SHARE_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SWAP_FEE_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TICK_SPACING",
    "outputs": [
      {
        "internalType": "int24",
        "name": "",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "WAD",
    "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 IPoolManager.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 IPoolManager.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 IPoolManager.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": [],
    "name": "bandReserve",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "bandState",
    "outputs": [
      {
        "components": [
          {
            "internalType": "uint160",
            "name": "sqrtFloorX96",
            "type": "uint160"
          },
          {
            "internalType": "uint160",
            "name": "sqrtPriceX96",
            "type": "uint160"
          },
          {
            "internalType": "uint160",
            "name": "sqrtUpperX96",
            "type": "uint160"
          },
          {
            "internalType": "uint256",
            "name": "liquidity",
            "type": "uint256"
          }
        ],
        "internalType": "struct Band.State",
        "name": "",
        "type": "tuple"
      }
    ],
    "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 IPoolManager.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": "",
        "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": "",
        "type": "uint160"
      }
    ],
    "name": "beforeInitialize",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct IPoolManager.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": "",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "bool",
            "name": "zeroForOne",
            "type": "bool"
          },
          {
            "internalType": "int256",
            "name": "amountSpecified",
            "type": "int256"
          },
          {
            "internalType": "uint160",
            "name": "sqrtPriceLimitX96",
            "type": "uint160"
          }
        ],
        "internalType": "struct IPoolManager.SwapParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "",
        "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": "burnedForever",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "curveMath",
    "outputs": [
      {
        "internalType": "contract SazareV2Math",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "curveScale",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "deepenSzr",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minScaleAdded",
        "type": "uint256"
      }
    ],
    "name": "deepenWeth",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "scaleAdded",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "feeRecipient",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "floorPrice",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "floorReserve",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "initialized",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "inventory",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "inventoryBacking",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "issuancePrice",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "issuanceScale",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "mintedEver",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pInit",
    "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": "uint256",
        "name": "gross",
        "type": "uint256"
      }
    ],
    "name": "quoteBuy",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "tokensOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "spotPriceAfter",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "totalFee",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokensOut",
        "type": "uint256"
      }
    ],
    "name": "quoteBuyExactTokens",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "grossWethIn",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "spotPriceAfter",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "totalFee",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "redeemableSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "saleCap",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "spotPrice",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "tailInventory",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "token",
    "outputs": [
      {
        "internalType": "contract SazareToken",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalDepthContributed",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalInventoryContributed",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalProtocolFeesPaid",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "weth",
    "outputs": [
      {
        "internalType": "contract ERC20",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {SazareToken} from "./SazareToken.sol";
import {FullMath} from "v4-core/libraries/FullMath.sol";
import {Hooks} from "v4-core/libraries/Hooks.sol";
import {TickMath} from "v4-core/libraries/TickMath.sol";
import {IHooks} from "v4-core/interfaces/IHooks.sol";
import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/types/PoolKey.sol";
import {Currency} from "v4-core/types/Currency.sol";
import {BeforeSwapDelta, toBeforeSwapDelta} from "v4-core/types/BeforeSwapDelta.sol";
import {BalanceDelta} from "v4-core/types/BalanceDelta.sol";
import {ERC20} from "solmate/src/tokens/ERC20.sol";
import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol";
import {Band} from "./libraries/Band.sol";
import {SazareV2Math} from "./SazareV2Math.sol";

/// @notice Sazare v2 custom-accounting market with a fixed issuance curve and
///         a permanently deepen-able full-range post-cap market.
/// @dev Fresh minting always uses issuanceScale. Permanent WETH contributions
///      increase secondary depth without cheapening issuance. At cap, one unified
///      virtual range spans from the live redemption floor to MAX_SQRT_PRICE.
contract SazareHookV2 is IHooks {
    using Hooks for IHooks;
    using SafeTransferLib for ERC20;

    uint256 public constant BPS = 10_000;
    uint256 public constant WAD = 1e18;
    uint256 public constant SWAP_FEE_BPS = 100;
    uint256 public constant FLOOR_FEE_SHARE_BPS = 7_000;
    /// @notice Baseline share of net buy input permanently assigned to the redemption floor.
    /// @dev When the live floor approaches spot, the floor receives more than this
    ///      minimum so that every token delivered remains fully floor backed.
    uint256 public constant FLOOR_BUY_SHARE_BPS = 5_000;
    uint256 public constant BAND_BUY_SHARE_BPS = BPS - FLOOR_BUY_SHARE_BPS;
    int24 public constant TICK_SPACING = 60;
    address public constant DEAD = 0x000000000000000000000000000000000000dEaD;

    error OnlyPoolManager();
    error InvalidPool();
    error AlreadyInitialized();
    error ExactOutput();
    error LiquidityForbidden();
    error InvalidParameter();
    error ZeroOutput();
    error CapExceeded();
    error AmountTooLarge();
    error InsufficientInventory();
    error InsufficientBandReserve();
    error SlippageExceeded();
    error NotPostCap();

    IPoolManager public immutable poolManager;
    ERC20 public immutable weth;
    SazareV2Math public immutable curveMath;
    SazareToken public immutable token;
    uint256 public immutable CAP;
    uint256 public immutable pInit;
    /// @notice Immutable exponential scale used only for newly minted supply.
    uint256 public immutable issuanceScale;
    /// @notice Supply sold on the issuance curve before the full-range tail is minted.
    uint256 public immutable saleCap;
    /// @notice Protocol-owned inventory minted at transition to seed post-cap asks.
    uint256 public immutable tailInventory;
    address public immutable feeRecipient;

    /// @notice Permanent floor backing. Never inferred from token balance.
    uint256 public floorReserve;
    /// @notice WETH backing the premium trading path and contributed depth.
    uint256 public bandReserve;
    /// @notice Floor-directed sell fees parked with non-redeemable inventory.
    /// @dev Activated into floorReserve pro rata when inventory returns to circulation.
    uint256 public inventoryBacking;
    /// @notice Tokens held by the hook and available for later buys.
    uint256 public inventory;
    /// @notice Lifetime minted amount. Burns never reopen cap room.
    uint256 public mintedEver;
    /// @notice Token-denominated secondary-band scale (18-decimal token units).
    /// @dev Starts equal to issuanceScale and only rises through deepenWeth.
    uint256 public curveScale;
    /// @notice Monotone start price for the next newly minted SZR.
    /// @dev Advances only when fresh supply is issued. Secondary depth and inventory
    ///      trading cannot lower it, so contributions cannot cheapen remaining issuance.
    uint256 public issuancePrice;
    /// @dev Stored curve spot. The effective spot is max(stored spot, live floor).
    uint256 private _curveSpotPrice;
    bool public initialized;
    uint256 public totalProtocolFeesPaid;
    uint256 public totalDepthContributed;
    uint256 public totalInventoryContributed;

    uint256 private _lastFloorBurn;
    uint256 private _lastProtocolFee;

    event ProtocolFeePaid(address indexed recipient, uint256 amount);
    event DepthAdded(address indexed contributor, uint256 wethAmount, uint256 scaleAdded, uint256 newScale);
    event InventoryDepthAdded(address indexed contributor, uint256 tokenAmount, uint256 newInventory);
    event SazareSwap(
        bool indexed isBuy, uint256 amountIn, uint256 amountOut, uint256 spotPriceAfter, uint256 floorPriceAfter
    );

    constructor(
        IPoolManager poolManager_,
        ERC20 weth_,
        SazareV2Math curveMath_,
        uint256 cap_,
        uint256 pInit_,
        uint256 curveScale_,
        uint256 tailInventory_,
        uint256 reserveSeed,
        uint256 dust,
        address reserveSeeder,
        address feeRecipient_,
        string memory tokenName_,
        string memory tokenSymbol_
    ) {
        if (
            address(poolManager_) == address(0) || address(weth_) == address(0) || address(curveMath_) == address(0)
                || address(curveMath_).codehash != keccak256(type(SazareV2Math).runtimeCode) || cap_ == 0 || pInit_ == 0
                || curveScale_ == 0 || reserveSeed == 0 || dust == 0 || dust > cap_ || reserveSeeder == address(0)
                || feeRecipient_ == address(0) || pInit_ > uint256(type(int256).max)
                || curveScale_ > uint256(type(int256).max) || tailInventory_ == 0 || tailInventory_ >= cap_ - dust
        ) revert InvalidParameter();
        if (curveScale_ >= cap_ - dust) revert InvalidParameter();

        poolManager = poolManager_;
        weth = weth_;
        curveMath = curveMath_;
        CAP = cap_;
        pInit = pInit_;
        issuanceScale = curveScale_;
        tailInventory = tailInventory_;
        saleCap = cap_ - tailInventory_;
        curveScale = curveScale_;
        issuancePrice = pInit_;
        _curveSpotPrice = pInit_;
        feeRecipient = feeRecipient_;
        token = new SazareToken(address(this), tokenName_, tokenSymbol_);

        floorReserve = reserveSeed;
        mintedEver = dust;
        token.mint(DEAD, dust);
        weth_.safeTransferFrom(reserveSeeder, address(this), reserveSeed);
        if (pInit_ * dust <= reserveSeed * WAD) revert InvalidParameter();

        IHooks(this).validateHookPermissions(_permissions());
    }

    function beforeInitialize(address, PoolKey calldata key, uint160) external override onlyManager returns (bytes4) {
        if (initialized) revert AlreadyInitialized();
        if (
            key.fee != 0 || key.tickSpacing != TICK_SPACING || address(key.hooks) != address(this)
                || !_isCanonicalPair(key.currency0, key.currency1)
        ) revert InvalidPool();
        initialized = true;
        return IHooks.beforeInitialize.selector;
    }

    function beforeAddLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata)
        external
        view
        override
        onlyManager
        returns (bytes4)
    {
        revert LiquidityForbidden();
    }

    function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)
        external
        override
        onlyManager
        returns (bytes4, BeforeSwapDelta, uint24)
    {
        if (!initialized || !_isCanonicalKey(key)) revert InvalidPool();
        if (params.amountSpecified >= 0) revert ExactOutput();
        uint256 amount = uint256(-params.amountSpecified);
        if (amount > uint256(uint128(type(int128).max))) revert AmountTooLarge();

        Currency input = params.zeroForOne ? key.currency0 : key.currency1;
        Currency output = params.zeroForOne ? key.currency1 : key.currency0;
        bool isBuy = Currency.unwrap(input) == address(weth);
        if (!isBuy && Currency.unwrap(input) != address(token)) revert InvalidPool();

        uint256 out = isBuy ? _buy(amount) : _sell(amount);
        if (out == 0 || out > uint256(uint128(type(int128).max))) revert ZeroOutput();

        poolManager.take(input, address(this), amount);
        _settle(output, out);

        if (_lastProtocolFee != 0) {
            uint256 protocolFee = _lastProtocolFee;
            _lastProtocolFee = 0;
            _payProtocolFee(protocolFee);
        }
        if (!isBuy && _lastFloorBurn != 0) {
            uint256 burnAmount = _lastFloorBurn;
            _lastFloorBurn = 0;
            token.burn(address(this), burnAmount);
        }

        if (mintedEver < CAP) _syncSpotToFloor();
        emit SazareSwap(isBuy, amount, out, spotPrice(), floorPrice());
        return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(uint128(amount)), -int128(uint128(out))), 0);
    }

    /// @notice Permanently contribute WETH to trading depth.
    /// @dev The contributor receives no shares and has no withdrawal claim. Before
    ///      cap this leaves spot unchanged. Post-cap the unified range is re-derived
    ///      from its holdings, so adding WETH raises its spot while floor is unchanged.
    function deepenWeth(uint256 amount, uint256 minScaleAdded) external returns (uint256 scaleAdded) {
        if (amount == 0) revert ZeroOutput();
        uint256 price = spotPrice();
        scaleAdded = FullMath.mulDiv(amount, WAD, price);
        if (scaleAdded == 0) revert ZeroOutput();
        if (scaleAdded < minScaleAdded) revert SlippageExceeded();

        weth.safeTransferFrom(msg.sender, address(this), amount);
        curveScale += scaleAdded;
        bandReserve += amount;
        totalDepthContributed += amount;
        emit DepthAdded(msg.sender, amount, scaleAdded, curveScale);
    }

    /// @notice Permanently contribute SZR to the post-cap full-range ask side.
    /// @dev The contributor receives no shares or withdrawal claim. Because the
    ///      tokens become non-redeemable inventory while floorReserve is unchanged,
    ///      the redemption floor rises immediately.
    function deepenSzr(uint256 amount) external {
        if (mintedEver < CAP) revert NotPostCap();
        if (amount == 0 || amount >= redeemableSupply()) revert InvalidParameter();

        ERC20(address(token)).safeTransferFrom(msg.sender, address(this), amount);
        inventory += amount;
        totalInventoryContributed += amount;
        emit InventoryDepthAdded(msg.sender, amount, inventory);
    }

    function redeemableSupply() public view returns (uint256) {
        return token.totalSupply() - inventory;
    }

    function burnedForever() external view returns (uint256) {
        return mintedEver - token.totalSupply();
    }

    function floorPrice() public view returns (uint256) {
        return FullMath.mulDiv(floorReserve, WAD, redeemableSupply());
    }

    function spotPrice() public view returns (uint256) {
        if (mintedEver >= CAP) return curveMath.priceFromSqrt(_postCapBandState().sqrtPriceX96);
        uint256 floor = floorPrice();
        return _curveSpotPrice < floor ? floor : _curveSpotPrice;
    }

    /// @notice Live post-cap virtual position on [floor, MAX_SQRT_PRICE].
    function bandState() external view returns (Band.State memory) {
        if (mintedEver < CAP) revert NotPostCap();
        return _postCapBandState();
    }

    function quoteBuy(uint256 gross)
        external
        view
        returns (uint256 tokensOut, uint256 spotPriceAfter, uint256 totalFee)
    {
        return _previewBuy(gross);
    }

    /// @notice Smallest estimated gross WETH input that delivers at least `tokensOut`.
    /// @dev Quote only; swaps remain exact-input. Primarily lets the final pre-cap
    ///      buyer size an input without donating an unbounded cap-clamp excess.
    function quoteBuyExactTokens(uint256 tokensOut)
        external
        view
        returns (uint256 grossWethIn, uint256 spotPriceAfter, uint256 totalFee)
    {
        if (mintedEver >= CAP) return _quotePostCapBuyExactTokens(tokensOut);

        uint256 room = _mintRoom();
        uint256 available = inventory + room;
        if (tokensOut == 0 || tokensOut > available) revert InsufficientInventory();

        uint256 price = spotPrice();
        uint256 initialPrice = price;
        uint256 roundingBandInput = curveScale / WAD + 1;
        uint256 fromInventory = tokensOut > inventory ? inventory : tokensOut;
        uint256 netNeeded;
        if (fromInventory != 0) {
            uint256 bandNet;
            (spotPriceAfter, bandNet) = _walkUpByTokens(price, fromInventory, curveScale);
            netNeeded = bandNet;
            price = spotPriceAfter;
        }

        uint256 fromMint = tokensOut - fromInventory;
        if (fromMint != 0) {
            uint256 mintNet;
            uint256 mintStart = _issuanceStartPrice();
            (spotPriceAfter, mintNet) = _walkUpByTokens(mintStart, fromMint, issuanceScale);
            netNeeded += mintNet;
            roundingBandInput = issuanceScale / WAD + 1;
        }
        if (fromMint == 0) spotPriceAfter = price;

        // `walkUpByTokens` rounds its endpoint up while `walkUpByNet` rounds
        // its endpoint down. Reserve four price-resolution units so an exact
        // token quote always delivers at least its requested amount even at a
        // floor-raised, high-price endpoint.
        (grossWethIn, totalFee) = curveMath.grossForNet(
            _netForBandInput(netNeeded + 4 * roundingBandInput, initialPrice)
        );
    }

    function _buy(uint256 gross) private returns (uint256 out) {
        uint256 spotAfter;
        uint256 fee;
        (out, spotAfter, fee) = _previewBuy(gross);
        uint256 floorFee = FullMath.mulDivRoundingUp(fee, FLOOR_FEE_SHARE_BPS, BPS);
        uint256 protocolFee = fee - floorFee;
        uint256 net = gross - fee;
        uint256 srPre = redeemableSupply();
        uint256 rectangle = FullMath.mulDivRoundingUp(out, floorReserve, srPre);
        uint256 bandInput = _bandBudget(net);
        uint256 floorInput = net - bandInput;
        if (rectangle > floorInput) revert ZeroOutput();

        uint256 inventoryPre = inventory;
        uint256 fromInventory = out > inventoryPre ? inventoryPre : out;
        uint256 fromMint = out - fromInventory;
        uint256 backingActivated;
        if (fromInventory != 0) {
            backingActivated = fromInventory == inventoryPre
                ? inventoryBacking
                : FullMath.mulDiv(inventoryBacking, fromInventory, inventoryPre);
            inventoryBacking -= backingActivated;
            inventory = inventoryPre - fromInventory;
        }
        if (fromMint != 0) {
            issuancePrice = spotAfter;
            mintedEver += fromMint;
            token.mint(address(this), fromMint);
            if (mintedEver == saleCap) {
                mintedEver = CAP;
                inventory += tailInventory;
                token.mint(address(this), tailInventory);
            }
        }

        floorReserve += floorInput + floorFee + backingActivated;
        bandReserve += bandInput;
        _curveSpotPrice = spotAfter;
        _lastProtocolFee = protocolFee;
    }

    function _previewBuy(uint256 gross)
        private
        view
        returns (uint256 tokensOut, uint256 spotPriceAfter, uint256 totalFee)
    {
        if (gross == 0) revert ZeroOutput();
        totalFee = FullMath.mulDivRoundingUp(gross, SWAP_FEE_BPS, BPS);
        uint256 net = gross - totalFee;
        if (net == 0) revert ZeroOutput();
        uint256 bandInput = _bandBudget(net);
        uint256 floorInput = net - bandInput;

        if (mintedEver >= CAP) return _previewPostCapBuy(bandInput, floorInput, totalFee);

        uint256 room = _mintRoom();
        uint256 available = inventory + room;
        if (available == 0) revert CapExceeded();

        uint256 price = spotPrice();
        uint256 remainingBandInput = bandInput;
        uint256 inventoryPre = inventory;
        if (inventoryPre != 0) {
            (uint256 inventoryEndSpot, uint256 inventoryNet) = _walkUpByTokens(price, inventoryPre, curveScale);
            if (remainingBandInput < inventoryNet) {
                (tokensOut, spotPriceAfter) = _walkUpByNet(price, remainingBandInput, curveScale);
                if (tokensOut > inventoryPre) tokensOut = inventoryPre;
                if (tokensOut == 0) revert ZeroOutput();
                return (tokensOut, spotPriceAfter, totalFee);
            }

            tokensOut = inventoryPre;
            price = inventoryEndSpot;
            remainingBandInput -= inventoryNet;
        }

        if (remainingBandInput != 0) {
            if (room == 0) {
                // `quoteBuyExactTokens` reserves four resolution units. Accept
                // that bounded exact-in surplus after a pure inventory quote.
                if (remainingBandInput > 4 * (curveScale / WAD + 1)) revert InsufficientInventory();
            } else if (FullMath.mulDiv(remainingBandInput, WAD, issuanceScale) == 0) {
                // A rounded exact-in quote can leave sub-resolution WETH after
                // inventory is exhausted. It is retained as band backing rather
                // than making an otherwise valid inventory purchase revert.
            } else {
                uint256 mintStart = _issuanceStartPrice();
                (uint256 mintedOut, uint256 mintEndSpot) = _walkUpByNet(mintStart, remainingBandInput, issuanceScale);
                if (mintedOut > room) {
                    (uint256 exactSpot, uint256 exactNet) = _walkUpByTokens(mintStart, room, issuanceScale);
                    // Keep the cap clamp consistent with the exact-token quote
                    // buffer. Without this, an otherwise valid final-cap quote
                    // can revert solely because of the quote's rounding reserve.
                    if (remainingBandInput > exactNet + 4 * (issuanceScale / WAD + 1)) {
                        revert InsufficientInventory();
                    }
                    mintedOut = room;
                    mintEndSpot = exactSpot;
                }
                tokensOut += mintedOut;
                price = mintEndSpot;
            }
        }
        spotPriceAfter = price;
        if (tokensOut == 0) revert ZeroOutput();
    }

    function _walkUpByNet(uint256 price, uint256 net, uint256 scale)
        private
        view
        returns (uint256 tokensOut, uint256 nextPrice)
    {
        (tokensOut, nextPrice) = curveMath.walkUpByNet(price, net, scale);
        if (tokensOut == 0 || nextPrice == price) revert ZeroOutput();
    }

    function _walkUpByTokens(uint256 price, uint256 tokens, uint256 scale)
        private
        view
        returns (uint256 nextPrice, uint256 netNeeded)
    {
        return curveMath.walkUpByTokens(price, tokens, scale);
    }

    /// @dev Fresh issuance is anchored only to its immutable issuance history and
    ///      the live redemption boundary. A secondary-inventory endpoint is not
    ///      an input: otherwise a WETH depth contribution could indirectly lower
    ///      the price at which the mint leg begins after inventory is cleared.
    function _issuanceStartPrice() private view returns (uint256 startPrice) {
        startPrice = issuancePrice;
        uint256 floor = floorPrice();
        if (startPrice < floor) startPrice = floor;
    }

    /// @dev The curve only spends the portion retained by the trading band.
    ///      The floor bound prevents a fixed allocation from under-backing output
    ///      when live spot is close to the redemption boundary.
    function _bandBudget(uint256 net) private view returns (uint256) {
        uint256 byShare = FullMath.mulDiv(net, BAND_BUY_SHARE_BPS, BPS);
        uint256 price = spotPrice();
        uint256 floor = floorPrice();
        uint256 byFloor = FullMath.mulDiv(net, price, price + floor);
        return byShare < byFloor ? byShare : byFloor;
    }

    function _netForBandInput(uint256 bandInput, uint256 price) private view returns (uint256 netNeeded) {
        uint256 byShare = FullMath.mulDivRoundingUp(bandInput, BPS, BAND_BUY_SHARE_BPS);
        uint256 byFloor = FullMath.mulDivRoundingUp(bandInput, price + floorPrice(), price);
        return byShare > byFloor ? byShare : byFloor;
    }

    function _previewPostCapBuy(uint256 bandInput, uint256 floorInput, uint256 totalFee)
        private
        view
        returns (uint256 tokensOut, uint256 spotPriceAfter, uint256)
    {
        if (inventory <= 1) revert CapExceeded();
        Band.State memory b = _postCapBandState();
        if (b.liquidity == 0 || b.sqrtPriceX96 >= TickMath.MAX_SQRT_PRICE) revert ZeroOutput();

        uint256 toMax = curveMath.amount1Delta(b.sqrtPriceX96, TickMath.MAX_SQRT_PRICE, b.liquidity, true);
        uint256 spent = bandInput < toMax ? bandInput : toMax;
        uint160 sqrtNext =
            spent == toMax ? TickMath.MAX_SQRT_PRICE : curveMath.nextFromAmount1In(b.sqrtPriceX96, b.liquidity, spent);
        if (sqrtNext > TickMath.MAX_SQRT_PRICE) sqrtNext = TickMath.MAX_SQRT_PRICE;
        tokensOut = curveMath.amount0Delta(b.sqrtPriceX96, sqrtNext, b.liquidity, false);

        uint256 maxTokens = inventory - 1;
        if (tokensOut > maxTokens) tokensOut = maxTokens;
        tokensOut = curveMath.capTokensByRectangle(tokensOut, floorInput, floorReserve, redeemableSupply());
        if (tokensOut == 0) revert ZeroOutput();

        uint256 geometricOut = curveMath.amount0Delta(b.sqrtPriceX96, sqrtNext, b.liquidity, false);
        if (tokensOut < geometricOut) {
            sqrtNext = curveMath.nextFromAmount0Out(b.sqrtPriceX96, b.liquidity, tokensOut);
        }
        spotPriceAfter = curveMath.priceFromSqrt(sqrtNext);
        return (tokensOut, spotPriceAfter, totalFee);
    }

    function _quotePostCapBuyExactTokens(uint256 tokensOut)
        private
        view
        returns (uint256 grossWethIn, uint256 spotPriceAfter, uint256 totalFee)
    {
        if (tokensOut == 0 || inventory <= 1 || tokensOut >= inventory) revert InsufficientInventory();
        Band.State memory b = _postCapBandState();
        if (b.liquidity == 0 || b.sqrtPriceX96 >= TickMath.MAX_SQRT_PRICE) revert ZeroOutput();

        uint160 sqrtNext = curveMath.nextFromAmount0Out(b.sqrtPriceX96, b.liquidity, tokensOut);
        if (sqrtNext > TickMath.MAX_SQRT_PRICE) sqrtNext = TickMath.MAX_SQRT_PRICE;
        uint256 bandInput = curveMath.amount1Delta(b.sqrtPriceX96, sqrtNext, b.liquidity, true);
        uint256 netNeeded = _netForBandInput(bandInput, curveMath.priceFromSqrt(b.sqrtPriceX96)) + 2;
        (grossWethIn, totalFee) = curveMath.grossForNet(netNeeded);
        spotPriceAfter = curveMath.priceFromSqrt(sqrtNext);
    }

    function _sellPostCap(uint256 amount) private returns (uint256 out) {
        if (amount == 0) revert ZeroOutput();
        uint256 remaining = amount;
        uint256 protocolFeeAccrued;

        if (bandReserve != 0) {
            Band.State memory b = _postCapBandState();
            if (b.liquidity != 0 && b.sqrtPriceX96 > b.sqrtFloorX96) {
                uint256 capacity = curveMath.amount0Delta(b.sqrtFloorX96, b.sqrtPriceX96, b.liquidity, false);
                uint256 bandTokens = remaining < capacity ? remaining : capacity;
                if (bandTokens != 0) {
                    uint256 grossCurve;
                    uint160 nextSqrt;
                    if (bandTokens == capacity) {
                        grossCurve = bandReserve;
                        bandReserve = 0;
                        nextSqrt = b.sqrtFloorX96;
                    } else {
                        nextSqrt = curveMath.nextFromAmount0In(b.sqrtPriceX96, b.liquidity, bandTokens);
                        if (nextSqrt < b.sqrtFloorX96) nextSqrt = b.sqrtFloorX96;
                        grossCurve = curveMath.amount1Delta(nextSqrt, b.sqrtPriceX96, b.liquidity, false);
                        if (grossCurve > bandReserve) grossCurve = bandReserve;
                        bandReserve -= grossCurve;
                    }

                    uint256 fee = FullMath.mulDivRoundingUp(grossCurve, SWAP_FEE_BPS, BPS);
                    uint256 floorFee = FullMath.mulDivRoundingUp(fee, FLOOR_FEE_SHARE_BPS, BPS);
                    uint256 protocolFee = fee - floorFee;
                    floorReserve += floorFee;
                    inventory += bandTokens;
                    remaining -= bandTokens;
                    out = grossCurve - fee;
                    protocolFeeAccrued = protocolFee;
                }
            }
        }

        if (remaining != 0) {
            uint256 sr = redeemableSupply();
            uint256 grossFloor = FullMath.mulDiv(remaining, floorReserve, sr);
            if (grossFloor == 0) revert ZeroOutput();
            uint256 fee = FullMath.mulDivRoundingUp(grossFloor, SWAP_FEE_BPS, BPS);
            uint256 protocolFee = fee - FullMath.mulDivRoundingUp(fee, FLOOR_FEE_SHARE_BPS, BPS);
            uint256 payout = grossFloor - fee;

            floorReserve -= payout + protocolFee;
            protocolFeeAccrued += protocolFee;
            _lastFloorBurn = remaining;
            out += payout;

            uint256 srAfterBurn = sr - remaining;
            if (srAfterBurn == 0) revert InvalidParameter();
        }

        _lastProtocolFee = protocolFeeAccrued;
        if (out == 0) revert ZeroOutput();
    }

    function _postCapBandState() private view returns (Band.State memory) {
        return curveMath.bandState(bandReserve, inventory, floorReserve, redeemableSupply());
    }

    function _mintRoom() private view returns (uint256) {
        return mintedEver < saleCap ? saleCap - mintedEver : 0;
    }

    function _sell(uint256 amount) private returns (uint256 out) {
        if (mintedEver >= CAP) return _sellPostCap(amount);
        if (amount == 0) revert ZeroOutput();
        uint256 remaining = amount;
        uint256 protocolFeeAccrued;
        uint256 price = spotPrice();
        uint256 floor = floorPrice();

        if (price > floor && bandReserve != 0) {
            uint256 capacity = curveMath.tokensToFloor(price, floor, curveScale);
            uint256 bandTokens = remaining < capacity ? remaining : capacity;
            if (bandTokens != 0) {
                (uint256 nextPrice, uint256 grossCurve) = curveMath.walkDownByTokens(price, bandTokens, curveScale);
                if (bandTokens == capacity || nextPrice < floor) nextPrice = floor;
                if (nextPrice == floor) grossCurve = FullMath.mulDiv(curveScale, price - nextPrice, WAD);
                uint256 srPre = redeemableSupply();
                uint256 floorRelease = FullMath.mulDiv(floorReserve, bandTokens, srPre);
                if (grossCurve < floorRelease) grossCurve = floorRelease;
                uint256 premium = grossCurve - floorRelease;
                if (premium > bandReserve) revert InsufficientBandReserve();

                uint256 fee = FullMath.mulDivRoundingUp(grossCurve, SWAP_FEE_BPS, BPS);
                uint256 floorFee = FullMath.mulDivRoundingUp(fee, FLOOR_FEE_SHARE_BPS, BPS);
                uint256 protocolFee = fee - floorFee;

                bandReserve -= premium;
                floorReserve = floorReserve - floorRelease + 1;
                inventoryBacking += floorFee - 1;
                inventory += bandTokens;
                remaining -= bandTokens;
                out += grossCurve - fee;
                protocolFeeAccrued += protocolFee;
                _curveSpotPrice = nextPrice;
            }
        }

        if (remaining != 0) {
            uint256 sr = redeemableSupply();
            uint256 grossFloor = FullMath.mulDiv(remaining, floorReserve, sr);
            if (grossFloor == 0) revert ZeroOutput();
            uint256 fee = FullMath.mulDivRoundingUp(grossFloor, SWAP_FEE_BPS, BPS);
            uint256 protocolFee = fee - FullMath.mulDivRoundingUp(fee, FLOOR_FEE_SHARE_BPS, BPS);
            uint256 payout = grossFloor - fee;

            floorReserve -= payout + protocolFee;
            protocolFeeAccrued += protocolFee;
            _lastFloorBurn = remaining;
            out += payout;

            uint256 srAfterBurn = sr - remaining;
            if (srAfterBurn == 0) revert InvalidParameter();
            _curveSpotPrice = FullMath.mulDiv(floorReserve, WAD, srAfterBurn);
        }

        _lastProtocolFee = protocolFeeAccrued;
        if (out == 0) revert ZeroOutput();
    }

    function _syncSpotToFloor() private {
        uint256 floor = floorPrice();
        if (_curveSpotPrice < floor) _curveSpotPrice = floor;
    }

    function _payProtocolFee(uint256 amount) private {
        if (amount == 0) return;
        totalProtocolFeesPaid += amount;
        weth.safeTransfer(feeRecipient, amount);
        emit ProtocolFeePaid(feeRecipient, amount);
    }

    function _settle(Currency currency, uint256 amount) private {
        poolManager.sync(currency);
        ERC20(Currency.unwrap(currency)).safeTransfer(address(poolManager), amount);
        poolManager.settle();
    }

    function _isCanonicalPair(Currency currency0, Currency currency1) private view returns (bool) {
        address w = address(weth);
        address t = address(token);
        return (w < t && Currency.unwrap(currency0) == w && Currency.unwrap(currency1) == t)
            || (t < w && Currency.unwrap(currency0) == t && Currency.unwrap(currency1) == w);
    }

    function _isCanonicalKey(PoolKey calldata key) private view returns (bool) {
        return key.fee == 0 && key.tickSpacing == TICK_SPACING && address(key.hooks) == address(this)
            && _isCanonicalPair(key.currency0, key.currency1);
    }

    function _permissions() private pure returns (Hooks.Permissions memory) {
        return Hooks.Permissions({
            beforeInitialize: true,
            afterInitialize: false,
            beforeAddLiquidity: true,
            afterAddLiquidity: false,
            beforeRemoveLiquidity: false,
            afterRemoveLiquidity: false,
            beforeSwap: true,
            afterSwap: false,
            beforeDonate: false,
            afterDonate: false,
            beforeSwapReturnDelta: true,
            afterSwapReturnDelta: false,
            afterAddLiquidityReturnDelta: false,
            afterRemoveLiquidityReturnDelta: false
        });
    }

    modifier onlyManager() {
        _onlyManager();
        _;
    }

    function _onlyManager() private view {
        if (msg.sender != address(poolManager)) revert OnlyPoolManager();
    }

    function afterInitialize(address, PoolKey calldata, uint160, int24) external pure override returns (bytes4) {
        revert InvalidPool();
    }

    function afterAddLiquidity(
        address,
        PoolKey calldata,
        IPoolManager.ModifyLiquidityParams calldata,
        BalanceDelta,
        BalanceDelta,
        bytes calldata
    ) external pure override returns (bytes4, BalanceDelta) {
        revert InvalidPool();
    }

    function beforeRemoveLiquidity(
        address,
        PoolKey calldata,
        IPoolManager.ModifyLiquidityParams calldata,
        bytes calldata
    ) external pure override returns (bytes4) {
        revert InvalidPool();
    }

    function afterRemoveLiquidity(
        address,
        PoolKey calldata,
        IPoolManager.ModifyLiquidityParams calldata,
        BalanceDelta,
        BalanceDelta,
        bytes calldata
    ) external pure override returns (bytes4, BalanceDelta) {
        revert InvalidPool();
    }

    function afterSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata)
        external
        pure
        override
        returns (bytes4, int128)
    {
        revert InvalidPool();
    }

    function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata)
        external
        pure
        override
        returns (bytes4)
    {
        revert InvalidPool();
    }

    function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata)
        external
        pure
        override
        returns (bytes4)
    {
        revert InvalidPool();
    }
}
Chain explorer4260msChain node86ms