FlowHookV3

0x6b9a1378c6730c87c4fc7f672208cd5f4c1920cc

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
16,430 bytes
ABI entries
70
30 read · 23 write
License
none

Contract information

Address
0x6b9a1378c6730c87c4fc7f672208cd5f4c1920cc
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xe6f121b746…c5f6eda735

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (30)

BPS()uint256
DEAD()address
LP_BAND()int24
MAX_FEE_BPS()uint256
MIN_FEE_BPS()uint256
POT_SHARE_BPS()uint256
SNIPE_GUARD()uint256
SNIPE_MAX_BPS()uint256
accruedBuyback(bytes32)uint256
accruedLP(bytes32)uint256
accruedPlatform()uint256
accruedRef(bytes32)uint256
accruedSnipe(bytes32)uint256
accruedTreasury(bytes32)uint256
accruedUsdg(bytes32)uint256
admin()address
burnPot()address
currentFeeBps(tuple)uint256
factory()address
getHookPermissions()tuple
keeper()address
launchAt(bytes32)uint64
platformCutBps(uint256)uint256
platformWallet()address
poolInitializer(tuple)address
poolManager()address
pools(bytes32)address, uint16, address, address, uint16, uint16, uint16, uint16, uint16, bool
refDistributor()address
referral()address
usdg()address

Events (11)

AccruedBurnPotSetBuybackBurnedFactorySetKeeperSetLPAddedPoolRegisteredRefDistributedRefDistributorSetReferralSetRouted

ABI

[
  {
    "inputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "_pm",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_usdg",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_platformWallet",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_admin",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "HookNotImplemented",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPool",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotPoolManager",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotSelf",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Accrued",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "pot",
        "type": "address"
      }
    ],
    "name": "BurnPotSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "usdgIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokensBurned",
        "type": "uint256"
      }
    ],
    "name": "BuybackBurned",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "factory",
        "type": "address"
      }
    ],
    "name": "FactorySet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "keeper",
        "type": "address"
      }
    ],
    "name": "KeeperSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "usdgIn",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "int24",
        "name": "tickLower",
        "type": "int24"
      },
      {
        "indexed": false,
        "internalType": "int24",
        "name": "tickUpper",
        "type": "int24"
      }
    ],
    "name": "LPAdded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint16",
        "name": "feeBps",
        "type": "uint16"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "treasury",
        "type": "address"
      }
    ],
    "name": "PoolRegistered",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "toReferrers",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "toHolders",
        "type": "uint256"
      }
    ],
    "name": "RefDistributed",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "refDistributor",
        "type": "address"
      }
    ],
    "name": "RefDistributorSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "address",
        "name": "referral",
        "type": "address"
      }
    ],
    "name": "ReferralSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "toHolders",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "toPlatform",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "toTreasury",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "toLP",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "toBuyback",
        "type": "uint256"
      }
    ],
    "name": "Routed",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "BPS",
    "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": "LP_BAND",
    "outputs": [
      {
        "internalType": "int24",
        "name": "",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_FEE_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MIN_FEE_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POT_SHARE_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SNIPE_GUARD",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SNIPE_MAX_BPS",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "accruedBuyback",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "accruedLP",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "accruedPlatform",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "accruedRef",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "accruedSnipe",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "accruedTreasury",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "accruedUsdg",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "admin",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct ModifyLiquidityParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "BalanceDelta",
        "name": "delta0",
        "type": "int256"
      },
      {
        "internalType": "BalanceDelta",
        "name": "delta1",
        "type": "int256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "afterAddLiquidity",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      },
      {
        "internalType": "BalanceDelta",
        "name": "",
        "type": "int256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "amount0",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount1",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "afterDonate",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      },
      {
        "internalType": "int24",
        "name": "tick",
        "type": "int24"
      }
    ],
    "name": "afterInitialize",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct ModifyLiquidityParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "BalanceDelta",
        "name": "delta0",
        "type": "int256"
      },
      {
        "internalType": "BalanceDelta",
        "name": "delta1",
        "type": "int256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "afterRemoveLiquidity",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      },
      {
        "internalType": "BalanceDelta",
        "name": "",
        "type": "int256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "bool",
            "name": "zeroForOne",
            "type": "bool"
          },
          {
            "internalType": "int256",
            "name": "amountSpecified",
            "type": "int256"
          },
          {
            "internalType": "uint160",
            "name": "sqrtPriceLimitX96",
            "type": "uint160"
          }
        ],
        "internalType": "struct SwapParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "BalanceDelta",
        "name": "delta",
        "type": "int256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "afterSwap",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      },
      {
        "internalType": "int128",
        "name": "",
        "type": "int128"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct ModifyLiquidityParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "beforeAddLiquidity",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "amount0",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "amount1",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "beforeDonate",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      }
    ],
    "name": "beforeInitialize",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "int24",
            "name": "tickLower",
            "type": "int24"
          },
          {
            "internalType": "int24",
            "name": "tickUpper",
            "type": "int24"
          },
          {
            "internalType": "int256",
            "name": "liquidityDelta",
            "type": "int256"
          },
          {
            "internalType": "bytes32",
            "name": "salt",
            "type": "bytes32"
          }
        ],
        "internalType": "struct ModifyLiquidityParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "beforeRemoveLiquidity",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "bool",
            "name": "zeroForOne",
            "type": "bool"
          },
          {
            "internalType": "int256",
            "name": "amountSpecified",
            "type": "int256"
          },
          {
            "internalType": "uint160",
            "name": "sqrtPriceLimitX96",
            "type": "uint160"
          }
        ],
        "internalType": "struct SwapParams",
        "name": "params",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "hookData",
        "type": "bytes"
      }
    ],
    "name": "beforeSwap",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      },
      {
        "internalType": "BeforeSwapDelta",
        "name": "",
        "type": "int256"
      },
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "burnPot",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      }
    ],
    "name": "currentFeeBps",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "address[]",
        "name": "buyers",
        "type": "address[]"
      },
      {
        "internalType": "uint256[]",
        "name": "amounts",
        "type": "uint256[]"
      },
      {
        "internalType": "uint256",
        "name": "toHolders",
        "type": "uint256"
      }
    ],
    "name": "distributeRef",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "factory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "flushPlatform",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      }
    ],
    "name": "flushTreasury",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "getHookPermissions",
    "outputs": [
      {
        "components": [
          {
            "internalType": "bool",
            "name": "beforeInitialize",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterInitialize",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeAddLiquidity",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterAddLiquidity",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeRemoveLiquidity",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterRemoveLiquidity",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeSwap",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterSwap",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeDonate",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterDonate",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "beforeSwapReturnDelta",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterSwapReturnDelta",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterAddLiquidityReturnDelta",
            "type": "bool"
          },
          {
            "internalType": "bool",
            "name": "afterRemoveLiquidityReturnDelta",
            "type": "bool"
          }
        ],
        "internalType": "struct Hooks.Permissions",
        "name": "",
        "type": "tuple"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      }
    ],
    "name": "harvest",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "keeper",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "launchAt",
    "outputs": [
      {
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "feeBps",
        "type": "uint256"
      }
    ],
    "name": "platformCutBps",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "platformWallet",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      }
    ],
    "name": "poolInitializer",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolManager",
    "outputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "pools",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "feeBps",
        "type": "uint16"
      },
      {
        "internalType": "address",
        "name": "initializer",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "treasury",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "holderBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "lpBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "treasuryBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "buybackBps",
        "type": "uint16"
      },
      {
        "internalType": "uint16",
        "name": "refBps",
        "type": "uint16"
      },
      {
        "internalType": "bool",
        "name": "registered",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "uint256",
        "name": "minOut",
        "type": "uint256"
      }
    ],
    "name": "processBuyback",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "tokensBurned",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      }
    ],
    "name": "processLP",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "refDistributor",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "referral",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint16",
        "name": "feeBps",
        "type": "uint16"
      },
      {
        "internalType": "address",
        "name": "initializer",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "treasury",
        "type": "address"
      },
      {
        "internalType": "uint16[5]",
        "name": "split",
        "type": "uint16[5]"
      }
    ],
    "name": "register",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "p",
        "type": "address"
      }
    ],
    "name": "setBurnPot",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "f",
        "type": "address"
      }
    ],
    "name": "setFactory",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "k",
        "type": "address"
      }
    ],
    "name": "setKeeper",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "d",
        "type": "address"
      }
    ],
    "name": "setRefDistributor",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "r",
        "type": "address"
      }
    ],
    "name": "setReferral",
    "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": "usdg",
    "outputs": [
      {
        "internalType": "contract IERC20",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

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

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    FlowHookV3 — v3 tax hook (contract #2)  ⚠️ WIP, NOT AUDITED

    Same 6909-claim skim + permissionless-flush engine as FlowHookSSb, but on the **USDG** leg (the token
    address is mined > USDG so USDG is always currency0 — the slot native ETH held in v2). It computes the
    tiered platform cut and splits the launcher's portion across 5 buckets.

    Per trade (tax = feeBps of the trade, taken in USDG):
      • platform cut = min(feeBps/2, feeBps ≤ 5% ? 0.5% : 1%). Of that: ~20% → the FLOW burn pot, ~80% → the
        platform wallet. (accrued → flushPlatform)
      • launcher portion = tax − platform cut, split by the pool's holder/lp/treasury/buyback/referral bps:
          - holders  → the token's baskets (inline USDG fundBaskets — trusted)
          - referral → holders for now (unreferred default; real attribution is a later step)
          - treasury → accrued (flushTreasury)
          - LP       → accrued (processLP, deferred: pool op)
          - buyback  → accrued (processBuyback, deferred: pool op)
      • the anti-snipe surcharge goes entirely to holders.
    Only the trusted token.fundBaskets call is inline; every other payout accrues and is flushed OUTSIDE the
    swap (reentrancy-safe). USDG is treated as a standard, non-reentrant ERC-20.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */

import {BaseHook} from "@openzeppelin/uniswap-hooks/src/base/BaseHook.sol";
import {CurrencySettler} from "@openzeppelin/uniswap-hooks/src/utils/CurrencySettler.sol";
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {IUnlockCallback} from "@uniswap/v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {SwapParams, ModifyLiquidityParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
import {BalanceDelta, BalanceDeltaLibrary} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {BeforeSwapDelta, BeforeSwapDeltaLibrary, toBeforeSwapDelta} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol";
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
import {LiquidityAmounts} from "@uniswap/v4-periphery/src/libraries/LiquidityAmounts.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface IFlowToken {
    function fundBaskets(uint256 amount) external;
}
interface IReferralRegistry {
    function credit(address user, uint256 amount) external;
}

contract FlowHookV3 is BaseHook, IUnlockCallback {
    using CurrencySettler for Currency;
    using BalanceDeltaLibrary for BalanceDelta;
    using SafeCast for uint256;
    using PoolIdLibrary for PoolKey;
    using StateLibrary for IPoolManager;
    using SafeERC20 for IERC20;

    uint256 public constant BPS = 10_000;
    uint256 public constant MIN_FEE_BPS = 50;    // 0.5%
    uint256 public constant MAX_FEE_BPS = 1_000; // 10%
    uint256 public constant POT_SHARE_BPS = 2_000; // 20% of the platform cut → burn pot

    uint256 public constant SNIPE_GUARD = 60;
    uint256 public constant SNIPE_MAX_BPS = 400;

    address public constant DEAD = 0x000000000000000000000000000000000000dEaD; // buyback burn sink
    int24 public constant LP_BAND = 6_000;       // width (ticks, 100 spacings) of a single-sided USDG sell-support band
    uint8 private constant ACT_REDEEM = 0;       // unlock actions
    uint8 private constant ACT_BUYBACK = 1;
    uint8 private constant ACT_LP = 2;

    IERC20 public immutable usdg;        // the base currency (currency0)
    address public immutable platformWallet;
    address public immutable admin;
    address public factory;
    address public keeper;               // LOW-trust: spot-touching bucket ops (buyback/LP) → pool/burn only
    address public refDistributor;       // FUND-CUSTODIAL: attributes + pays the referral bucket (distributeRef)
    address public burnPot;              // FLOW burn pot (settable once); until set, the pot share → platform wallet
    address public referral;             // ReferralRegistry (settable once); until set, the ref slice folds → holders

    struct PoolConfig {
        address token;
        uint16 feeBps;
        address initializer;
        address treasury;
        uint16 holderBps;   // split of the LAUNCHER portion (the 5 sum to BPS)
        uint16 lpBps;
        uint16 treasuryBps;
        uint16 buybackBps;
        uint16 refBps;
        bool registered;
    }
    mapping(bytes32 => PoolConfig) public pools;
    mapping(bytes32 => uint256) public accruedUsdg;      // USDG-claim fees per pool, awaiting flush
    mapping(bytes32 => uint256) public accruedSnipe;     // of accruedUsdg, the surcharge portion (→ holders)
    mapping(bytes32 => uint256) public accruedTreasury;  // real USDG owed to a pool's treasury
    mapping(bytes32 => uint256) public accruedLP;        // real USDG for LP auto-add (processLP — deferred)
    mapping(bytes32 => uint256) public accruedBuyback;   // real USDG for buyback&burn (processBuyback)
    mapping(bytes32 => uint256) public accruedRef;       // real USDG for referral payout (distributeRef); unref → holders
    mapping(bytes32 => uint64) public launchAt;
    uint256 public accruedPlatform;                      // real USDG owed to platform wallet + burn pot
    bool private _internalOp;                             // set while the hook swaps on its OWN pool (buyback) → skip the tax

    event FactorySet(address factory);
    event BurnPotSet(address pot);
    event KeeperSet(address keeper);
    event RefDistributorSet(address refDistributor);
    event ReferralSet(address referral);
    event RefDistributed(address indexed token, uint256 toReferrers, uint256 toHolders);
    event PoolRegistered(bytes32 indexed poolId, address token, uint16 feeBps, address treasury);
    event Accrued(bytes32 indexed poolId, uint256 amount);
    event Routed(address indexed token, uint256 toHolders, uint256 toPlatform, uint256 toTreasury, uint256 toLP, uint256 toBuyback);
    event BuybackBurned(address indexed token, uint256 usdgIn, uint256 tokensBurned);
    event LPAdded(address indexed token, uint256 usdgIn, int24 tickLower, int24 tickUpper);

    constructor(IPoolManager _pm, address _usdg, address _platformWallet, address _admin) BaseHook(_pm) {
        require(_usdg != address(0) && _platformWallet != address(0) && _admin != address(0), "zero");
        usdg = IERC20(_usdg);
        platformWallet = _platformWallet;
        admin = _admin;
        keeper = _admin;         // default; admin delegates to a low-trust backend keeper
        refDistributor = _admin; // default; admin delegates to a secured (multisig) referral distributor
    }

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

    // ─────────────────────────── admin ───────────────────────────
    function setFactory(address f) external {
        require(msg.sender == admin && factory == address(0) && f != address(0), "set");
        factory = f; emit FactorySet(f);
    }
    function setBurnPot(address p) external {
        require(msg.sender == admin && burnPot == address(0) && p != address(0), "set");
        burnPot = p; emit BurnPotSet(p);
    }
    /// Wire the ReferralRegistry (one-shot). The hook must also be set as the registry's distributor so it can
    /// credit referrers. Until set, the referral slice folds into holders (see _route).
    function setReferral(address r) external {
        require(msg.sender == admin && referral == address(0) && r != address(0), "set");
        referral = r; emit ReferralSet(r);
    }
    /// The keeper triggers the spot-touching bucket ops (processBuyback/processLP) with a fair, quoted minOut,
    /// so a third party can't sandwich a permissionless flush. It can move funds ONLY into the pool or the burn
    /// sink (DEAD) — never to an arbitrary party; worst case a bad keeper skips the op (USDG just keeps accruing,
    /// never lost). Referral payouts (which DO pay arbitrary referrers) are a SEPARATE, higher-trust role — see
    /// refDistributor — so this keeper can stay a low-trust hot key.
    function setKeeper(address k) external {
        require(msg.sender == admin && k != address(0), "auth");
        keeper = k; emit KeeperSet(k);
    }
    /// The referral distributor attributes referred buys off-chain and calls distributeRef; because it can direct
    /// the referral bucket to referrer addresses, it is FUND-CUSTODIAL for that bucket — secure it accordingly
    /// (ideally a multisig), NOT the same low-trust hot key as the keeper.
    function setRefDistributor(address d) external {
        require(msg.sender == admin && d != address(0), "auth");
        refDistributor = d; emit RefDistributorSet(d);
    }

    /// split bps: [holder, lp, treasury, buyback, ref] must sum to BPS.
    function register(PoolKey calldata key, address token, uint16 feeBps, address initializer, address treasury, uint16[5] calldata split) external {
        require(msg.sender == factory, "only factory");
        require(feeBps >= MIN_FEE_BPS && feeBps <= MAX_FEE_BPS, "fee range");
        require(initializer != address(0) && token != address(0) && treasury != address(0), "zero");
        require(uint256(split[0]) + split[1] + split[2] + split[3] + split[4] == BPS, "split sum");
        require(Currency.unwrap(key.currency0) == address(usdg) && Currency.unwrap(key.currency1) == token, "key");
        require(key.fee == 3000 && key.tickSpacing == 60 && address(key.hooks) == address(this), "key params");
        bytes32 id = PoolId.unwrap(key.toId());
        require(!pools[id].registered, "registered");
        pools[id] = PoolConfig(token, feeBps, initializer, treasury, split[0], split[1], split[2], split[3], split[4], true);
        emit PoolRegistered(id, token, feeBps, treasury);
    }

    function poolInitializer(PoolKey calldata key) external view returns (address) {
        return pools[PoolId.unwrap(key.toId())].initializer;
    }
    function currentFeeBps(PoolKey calldata key) external view returns (uint256) {
        bytes32 id = PoolId.unwrap(key.toId());
        return pools[id].feeBps + _snipeBps(id);
    }
    /// the platform cut in bps for a given tax: min(feeBps/2, cap) — cap 0.5% ≤5% tax, 1% above.
    function platformCutBps(uint256 feeBps) public pure returns (uint256) {
        uint256 cap = feeBps <= 500 ? 50 : 100;
        uint256 half = feeBps / 2;
        return half < cap ? half : cap;
    }

    function _beforeInitialize(address sender, PoolKey calldata key, uint160) internal override returns (bytes4) {
        bytes32 id = PoolId.unwrap(key.toId());
        PoolConfig storage cfg = pools[id];
        require(cfg.registered && sender == cfg.initializer, "not initializer");
        launchAt[id] = uint64(block.timestamp);
        return this.beforeInitialize.selector;
    }
    function _snipeBps(bytes32 id) internal view returns (uint256) {
        uint64 t = launchAt[id];
        if (t == 0) return 0;
        uint256 elapsed = block.timestamp - t;
        if (elapsed >= SNIPE_GUARD) return 0;
        return (SNIPE_MAX_BPS * (SNIPE_GUARD - elapsed)) / SNIPE_GUARD;
    }

    // ─────────────────────────── beforeSwap (BUY exact-in USDG) ───────────────────────────
    function _beforeSwap(address, PoolKey calldata key, SwapParams calldata params, bytes calldata)
        internal override returns (bytes4, BeforeSwapDelta, uint24)
    {
        if (_internalOp) return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); // hook's own buyback → no tax
        bytes32 id = PoolId.unwrap(key.toId());
        PoolConfig storage cfg = pools[id];
        if (cfg.registered && params.zeroForOne && params.amountSpecified < 0) { // USDG in
            uint256 amt = uint256(-params.amountSpecified);
            uint256 base = (amt * cfg.feeBps) / BPS;
            uint256 snipe = (amt * _snipeBps(id)) / BPS;
            uint256 fee = base + snipe;
            if (fee > 0) {
                key.currency0.take(poolManager, address(this), fee, true);
                accruedUsdg[id] += fee; accruedSnipe[id] += snipe;
                emit Accrued(id, fee);
                _flush(id, key);
                return (this.beforeSwap.selector, toBeforeSwapDelta(fee.toInt128(), 0), 0);
            }
        }
        return (this.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);
    }

    // ─────────────────────────── afterSwap (sells / exact-out) ───────────────────────────
    function _afterSwap(address, PoolKey calldata key, SwapParams calldata params, BalanceDelta delta, bytes calldata)
        internal override returns (bytes4, int128)
    {
        if (_internalOp) return (this.afterSwap.selector, int128(0)); // hook's own buyback → no tax
        bytes32 id = PoolId.unwrap(key.toId());
        PoolConfig storage cfg = pools[id];
        bool buyExactIn = params.zeroForOne && params.amountSpecified < 0;
        if (!cfg.registered || buyExactIn) return (this.afterSwap.selector, int128(0));

        bool exactIn = params.amountSpecified < 0;
        bool specifiedIs0 = exactIn ? params.zeroForOne : !params.zeroForOne;
        bool unspecifiedIs0 = !specifiedIs0;

        // We can ONLY charge a fee we can redeem: the hook has no path to redeem currency1 (token) 6909 claims,
        // so an exact-OUTPUT sell (unspecified leg = token) is left untaxed. Never take() a currency with no
        // redemption path — doing so would freeze the fee as unredeemable claims and credit no bucket.
        if (!unspecifiedIs0) return (this.afterSwap.selector, int128(0));

        uint256 mag = delta.amount0() < 0 ? uint256(uint128(-delta.amount0())) : uint256(uint128(delta.amount0()));
        uint256 base = (mag * cfg.feeBps) / BPS;
        uint256 snipe = (mag * _snipeBps(id)) / BPS;
        uint256 fee = base + snipe;
        if (fee == 0) return (this.afterSwap.selector, int128(0));

        key.currency0.take(poolManager, address(this), fee, true);
        accruedUsdg[id] += fee; accruedSnipe[id] += snipe; emit Accrued(id, fee);
        _flush(id, key);
        return (this.afterSwap.selector, fee.toInt128());
    }

    function _flush(bytes32 id, PoolKey calldata key) internal {
        uint256 amt = accruedUsdg[id];
        if (amt == 0 || usdg.balanceOf(address(poolManager)) < amt) return;
        uint256 snipe = accruedSnipe[id];
        accruedUsdg[id] = 0; accruedSnipe[id] = 0;
        poolManager.burn(address(this), key.currency0.toId(), amt);
        key.currency0.take(poolManager, address(this), amt, false); // pull real USDG here
        _route(id, amt - snipe, snipe);
    }

    /// permissionless: redeem a pool's accrued USDG claims → real USDG → route.
    function harvest(PoolKey calldata key) external {
        bytes32 id = PoolId.unwrap(key.toId());
        uint256 amount = accruedUsdg[id];
        require(amount > 0, "nothing");
        uint256 snipe = accruedSnipe[id];
        accruedUsdg[id] = 0; accruedSnipe[id] = 0;
        poolManager.unlock(abi.encode(ACT_REDEEM, key.currency0, amount));
        _route(id, amount - snipe, snipe);
    }
    function unlockCallback(bytes calldata data) external override returns (bytes memory) {
        require(msg.sender == address(poolManager), "auth");
        uint8 action = abi.decode(data, (uint8));
        if (action == ACT_REDEEM) {
            (, Currency cur, uint256 amount) = abi.decode(data, (uint8, Currency, uint256));
            poolManager.burn(address(this), cur.toId(), amount);
            cur.take(poolManager, address(this), amount, false);
            return "";
        }
        if (action == ACT_BUYBACK) {
            (, PoolKey memory key, uint256 amt, uint256 minOut) = abi.decode(data, (uint8, PoolKey, uint256, uint256));
            _internalOp = true;
            BalanceDelta d = poolManager.swap(
                key,
                SwapParams({zeroForOne: true, amountSpecified: -int256(amt), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}),
                ""
            );
            _internalOp = false;
            uint256 paid = uint256(uint128(-d.amount0()));      // USDG owed to the pool
            uint256 out = uint256(uint128(d.amount1()));         // token received
            require(out >= minOut, "slippage");
            key.currency0.settle(poolManager, address(this), paid, false); // pay real USDG
            key.currency1.take(poolManager, DEAD, out, false);             // burn the token
            return abi.encode(paid, out);
        }
        // ACT_LP: single-sided USDG sell-support band strictly ABOVE spot (token0-only → no token needed).
        (, PoolKey memory k, uint256 lpAmt) = abi.decode(data, (uint8, PoolKey, uint256));
        (, int24 tick, , ) = poolManager.getSlot0(k.toId());
        int24 spacing = k.tickSpacing;
        int24 lower = _alignUp(tick + 1, spacing);
        int24 maxT = TickMath.maxUsableTick(spacing);
        int24 upper = lower + LP_BAND > maxT ? maxT : lower + LP_BAND;
        require(lower < upper, "lp range");
        uint128 liq = LiquidityAmounts.getLiquidityForAmount0(
            TickMath.getSqrtPriceAtTick(lower), TickMath.getSqrtPriceAtTick(upper), lpAmt
        );
        require(liq > 0, "lp dust");
        (BalanceDelta ld, ) = poolManager.modifyLiquidity(
            k, ModifyLiquidityParams({tickLower: lower, tickUpper: upper, liquidityDelta: int256(uint256(liq)), salt: bytes32(0)}), ""
        );
        uint256 spent = ld.amount0() < 0 ? uint256(uint128(-ld.amount0())) : 0;
        if (spent > 0) k.currency0.settle(poolManager, address(this), spent, false);
        // a token-side owe would mean the band straddled spot; lower>tick guarantees it never does.
        require(ld.amount1() >= 0, "lp straddle");
        return abi.encode(spent, uint256(uint24(lower)) | (uint256(uint24(upper)) << 24));
    }

    /// Round `x` up to the nearest multiple of `s` (works for negative ticks).
    function _alignUp(int24 x, int24 s) internal pure returns (int24 r) {
        r = (x / s) * s;
        if (r < x) r += s;
    }

    /// Split a redeemed BASE (USDG) amount across the 5 buckets + platform. Only the trusted token
    /// fundBaskets is inline; platform/treasury/LP/buyback accrue and flush outside the swap.
    function _route(bytes32 id, uint256 base, uint256 snipe) internal {
        PoolConfig storage cfg = pools[id];
        uint256 platCut = (base * platformCutBps(cfg.feeBps)) / cfg.feeBps; // tiered platform cut in USDG
        uint256 launcher = base - platCut;
        uint256 toHolders = (launcher * cfg.holderBps) / BPS;
        uint256 toLP = (launcher * cfg.lpBps) / BPS;
        uint256 toTreasury = (launcher * cfg.treasuryBps) / BPS;
        uint256 toBuyback = (launcher * cfg.buybackBps) / BPS;
        uint256 toRef = launcher - toHolders - toLP - toTreasury - toBuyback; // referral remainder
        uint256 holderTotal = toHolders + snipe;

        // The referral slice accrues for the distributor to pay referrers (unreferred buys → holders, via
        // distributeRef). BEFORE a registry is wired, or when there's no ref slice, it folds into holders —
        // this is the safe default and preserves behaviour for pools without referrals.
        if (referral == address(0) || toRef == 0) holderTotal += toRef;
        else accruedRef[id] += toRef;

        accruedPlatform += platCut;
        accruedTreasury[id] += toTreasury;
        accruedLP[id] += toLP;
        accruedBuyback[id] += toBuyback;
        if (holderTotal > 0) {
            usdg.forceApprove(cfg.token, holderTotal);
            IFlowToken(cfg.token).fundBaskets(holderTotal); // pulls USDG via transferFrom
        }
        emit Routed(cfg.token, holderTotal, platCut, toTreasury, toLP, toBuyback);
    }

    // ─────────────────────────── flushes (permissionless, outside swaps) ───────────────────────────
    function flushPlatform() external {
        uint256 amt = accruedPlatform;
        require(amt > 0, "nothing");
        accruedPlatform = 0;
        uint256 pot = (burnPot != address(0)) ? (amt * POT_SHARE_BPS) / BPS : 0;
        if (pot > 0) usdg.safeTransfer(burnPot, pot);
        usdg.safeTransfer(platformWallet, amt - pot);
    }
    function flushTreasury(PoolKey calldata key) external {
        bytes32 id = PoolId.unwrap(key.toId());
        uint256 amt = accruedTreasury[id];
        require(amt > 0, "nothing");
        accruedTreasury[id] = 0;
        usdg.safeTransfer(pools[id].treasury, amt);
    }

    /// Permissionless: spend a pool's accrued buyback USDG on its own token and burn it (→ DEAD).
    /// `minOut` bounds slippage; a keeper quotes it. Callers who pass 0 risk being sandwiched.
    function processBuyback(PoolKey calldata key, uint256 minOut) external returns (uint256 tokensBurned) {
        require(msg.sender == keeper, "keeper"); // spot-touching op: keeper quotes a fair minOut (no third-party sandwich)
        bytes32 id = PoolId.unwrap(key.toId());
        require(pools[id].registered, "unregistered");
        uint256 amt = accruedBuyback[id];
        require(amt > 0, "nothing");
        accruedBuyback[id] = 0;
        (uint256 paid, uint256 out) = abi.decode(
            poolManager.unlock(abi.encode(ACT_BUYBACK, key, amt, minOut)), (uint256, uint256)
        );
        if (paid < amt) accruedBuyback[id] += amt - paid; // refund any unspent dust
        emit BuybackBurned(pools[id].token, paid, out);
        return out;
    }

    /// Permissionless: add a pool's accrued LP USDG as single-sided sell-support liquidity above spot,
    /// owned by the hook and never removed (permanently locked). Unspent dust is re-accrued.
    function processLP(PoolKey calldata key) external {
        require(msg.sender == keeper, "keeper"); // spot-touching op: keeper calls it at a fair tick (no manipulation sandwich)
        bytes32 id = PoolId.unwrap(key.toId());
        require(pools[id].registered, "unregistered");
        uint256 amt = accruedLP[id];
        require(amt > 0, "nothing");
        accruedLP[id] = 0;
        (uint256 spent, uint256 packed) = abi.decode(
            poolManager.unlock(abi.encode(ACT_LP, key, amt)), (uint256, uint256)
        );
        if (spent < amt) accruedLP[id] += amt - spent;
        emit LPAdded(pools[id].token, spent, int24(uint24(packed)), int24(uint24(packed >> 24)));
    }

    /// Pay out a pool's accrued referral USDG. The refDistributor reads referred buys off-chain (buyer→referrer
    /// via the ReferralRegistry) and passes each referred `buyers[i]` + their earned `amounts[i]`; `toHolders` is
    /// the unreferred remainder that folds back to holder baskets. A credit that fails (e.g. the buyer's
    /// referrer got unset) is skipped and its amount folds to holders too, so one bad entry can't block the
    /// batch. Bounded by accruedRef so the keeper can never distribute more than the pool actually skimmed.
    function distributeRef(PoolKey calldata key, address[] calldata buyers, uint256[] calldata amounts, uint256 toHolders) external {
        require(msg.sender == refDistributor, "refdist"); // fund-custodial role, separate from the low-trust keeper
        require(buyers.length == amounts.length, "len");
        bytes32 id = PoolId.unwrap(key.toId());
        PoolConfig storage cfg = pools[id];
        require(cfg.registered && referral != address(0), "unavailable");

        uint256 total = toHolders;
        for (uint256 i; i < amounts.length; ++i) total += amounts[i];
        require(total <= accruedRef[id], "exceeds accrued");
        accruedRef[id] -= total;

        uint256 credited;
        for (uint256 i; i < buyers.length; ++i) {
            uint256 amt = amounts[i];
            if (amt == 0) continue;
            usdg.forceApprove(referral, amt);
            try IReferralRegistry(referral).credit(buyers[i], amt) { credited += amt; }
            catch { usdg.forceApprove(referral, 0); } // failed → folds to holders below
        }
        uint256 toBaskets = total - credited; // intended unreferred remainder + any failed credits
        if (toBaskets > 0) {
            usdg.forceApprove(cfg.token, toBaskets);
            IFlowToken(cfg.token).fundBaskets(toBaskets);
        }
        emit RefDistributed(cfg.token, credited, toBaskets);
    }
}
Chain explorer3613msChain node81ms