KaleidoToken

0x6689ab375aaaa9aed78aaaba2e8edcc547478cf1

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
14,849 bytes
ABI entries
109
43 read · 19 write
License
mit

Contract information

Address
0x6689ab375aaaa9aed78aaaba2e8edcc547478cf1
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x6bba72749b…93633d805e

Token

Name
Kaleido
Symbol
KALEIDO
Decimals
18
Holders
634

Read contract (43)

DOMAIN_SEPARATOR()bytes32
POOL_FEE()uint24
SUPPLY()uint256
TICK_SPACING()int24
UNIT()uint256
accEthPerShare()uint256
allowance(address, address)uint256
balanceOf(address)uint256
bountyFor(uint256)uint256
claimRouter()address
decimals()uint8
dev()address
devOwed()uint256
feeDebt(uint256)uint256
forfeitedWei()uint256
globalTickLower()int24
globalTickUpper()int24
kaleidoIsToken0()bool
mintedAt(uint256)uint256
mirror()address
name()string
nftBalanceOf(address)uint256
nftContractURI()string
nftGetApproved(uint256)address
nftIsApprovedForAll(address, address)bool
nftOwnerOf(uint256)address
nftTokenURI(uint256)string
nonces(address)uint256
npm()address
onERC721Received(address, address, uint256, bytes)bytes4
ownedTokensOf(address)uint256[]
owner()address
ownershipHandoverExpiresAt(address)uint256
pendingFees(uint256)uint256
pool()address
positionTokenId()uint256
renderer()address
seeded()bool
symbol()string
totalShares()uint256
totalSupply()uint256
v3Factory()address
weth()address

Events (15)

ApprovalClaimRouterSetDevWithdrawnFeesHarvestedFeesSettledForfeitedForfeitsSweptNFTBurnedNFTMintedOwnershipHandoverCanceledOwnershipHandoverRequestedOwnershipTransferredRendererSetSeededTransfer

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_v3Factory",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_npm",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_weth",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_dev",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "AllowanceOverflow",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "AllowanceUnderflow",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "AlreadyInitialized",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "AlreadySeeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ClaimRouterAlreadySet",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ClaimRouterOnly",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "EthTransferFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ExcludedShareRecipient",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientAllowance",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InsufficientBalance",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPermit",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidTokenId",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MirrorOnly",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NewOwnerIsZeroAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NoHandoverRequest",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotDev",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotOwnerOrApproved",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotShareOwner",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Permit2AllowanceIsFixedAtInfinity",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PermitExpired",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Reentrancy",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "RouterHandshakeFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SeedNotSingleSided",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SeedPoolMismatch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SeedPriceMismatch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SelfTransferDisallowed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TotalSupplyOverflow",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TransferToZero",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Unauthorized",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ZeroAddress",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Approval",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "router",
        "type": "address"
      }
    ],
    "name": "ClaimRouterSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "DevWithdrawn",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethToHolders",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethToDev",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "bounty",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenBurned",
        "type": "uint256"
      }
    ],
    "name": "FeesHarvested",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "holder",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethOut",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenCount",
        "type": "uint256"
      }
    ],
    "name": "FeesSettled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Forfeited",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethToHolders",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "bounty",
        "type": "uint256"
      }
    ],
    "name": "ForfeitsSwept",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "NFTBurned",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "NFTMinted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "pendingOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipHandoverCanceled",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "pendingOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipHandoverRequested",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "oldOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferred",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "renderer",
        "type": "address"
      }
    ],
    "name": "RendererSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "posmTokenId",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      },
      {
        "indexed": false,
        "internalType": "uint128",
        "name": "liquidity",
        "type": "uint128"
      }
    ],
    "name": "Seeded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Transfer",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "DOMAIN_SEPARATOR",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "result",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "POOL_FEE",
    "outputs": [
      {
        "internalType": "uint24",
        "name": "",
        "type": "uint24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "SUPPLY",
    "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": "UNIT",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "accEthPerShare",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      }
    ],
    "name": "allowance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "approve",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "balanceOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "ethGained",
        "type": "uint256"
      }
    ],
    "name": "bountyFor",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "b",
        "type": "uint256"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "cancelOwnershipHandover",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "claimRouter",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pendingOwner",
        "type": "address"
      }
    ],
    "name": "completeOwnershipHandover",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "decimals",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "dev",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "devOwed",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "feeDebt",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "forfeitedWei",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "globalTickLower",
    "outputs": [
      {
        "internalType": "int24",
        "name": "",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "globalTickUpper",
    "outputs": [
      {
        "internalType": "int24",
        "name": "",
        "type": "int24"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "caller",
        "type": "address"
      }
    ],
    "name": "handleNFTApprove",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "operator",
        "type": "address"
      },
      {
        "internalType": "bool",
        "name": "approved",
        "type": "bool"
      },
      {
        "internalType": "address",
        "name": "caller",
        "type": "address"
      }
    ],
    "name": "handleNFTSetApprovalForAll",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "caller",
        "type": "address"
      }
    ],
    "name": "handleNFTTransfer",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "kaleidoIsToken0",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "mintedAt",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "mirror",
    "outputs": [
      {
        "internalType": "contract KaleidoMirror",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner_",
        "type": "address"
      }
    ],
    "name": "nftBalanceOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "nftContractURI",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "nftGetApproved",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "o",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "s",
        "type": "address"
      }
    ],
    "name": "nftIsApprovedForAll",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "nftOwnerOf",
    "outputs": [
      {
        "internalType": "address",
        "name": "o",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "nftTokenURI",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "nonces",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "npm",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }
    ],
    "name": "onERC721Received",
    "outputs": [
      {
        "internalType": "bytes4",
        "name": "",
        "type": "bytes4"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner_",
        "type": "address"
      }
    ],
    "name": "ownedTokensOf",
    "outputs": [
      {
        "internalType": "uint256[]",
        "name": "ids",
        "type": "uint256[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "result",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "pendingOwner",
        "type": "address"
      }
    ],
    "name": "ownershipHandoverExpiresAt",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "pendingFees",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "deadline",
        "type": "uint256"
      },
      {
        "internalType": "uint8",
        "name": "v",
        "type": "uint8"
      },
      {
        "internalType": "bytes32",
        "name": "r",
        "type": "bytes32"
      },
      {
        "internalType": "bytes32",
        "name": "s",
        "type": "bytes32"
      }
    ],
    "name": "permit",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pokeFees",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pool",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "positionTokenId",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renderer",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "requestOwnershipHandover",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint160",
        "name": "sqrtPriceX96",
        "type": "uint160"
      },
      {
        "internalType": "int24",
        "name": "tickLower",
        "type": "int24"
      },
      {
        "internalType": "int24",
        "name": "tickUpper",
        "type": "int24"
      }
    ],
    "name": "seed",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "seeded",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "router",
        "type": "address"
      }
    ],
    "name": "setClaimRouter",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newRenderer",
        "type": "address"
      }
    ],
    "name": "setRenderer",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256[]",
        "name": "tokenIds",
        "type": "uint256[]"
      },
      {
        "internalType": "address",
        "name": "holder",
        "type": "address"
      }
    ],
    "name": "settleClaim",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "ethOut",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "sweepForfeits",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "symbol",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalShares",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "totalSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "result",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "transfer",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "transferFrom",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "v3Factory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "weth",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }
    ],
    "name": "withdrawDev",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

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

// Derived from Prism's PrismHook (https://github.com/0xsolazy/prism, MIT © 0xsolazy). See docs/fork-map.md.
// KALEIDO — the protocol token ($KALEIDO) and its NFT "Shares" (final brand, VEG2-1148; historical code name
// "Quotra/QOTA" survives only in git history / changelog).
//
// The Kaleido core (monolith, per fork-map §5): ERC-20 (5000 KALEIDO) + dual ERC-20/721 NFT accounting
// (the Shares) + owner of the protocol LP position. Forked from PrismHook with the v4 hook REMOVED —
// the KALEIDO/ETH pool is vanilla (hooks = address(0)); closes the SATO drift class.
//
// v1.1 "Choose your dividend" (amendment 10/07): no vote, no epochs, no calendar. The Prism-vanilla per-share
// ETH accumulator (accEthPerShare + feeDebt) returns in VEG2-1121/1125 (pokeFees credits it directly); holders
// pick their stock at claim time (ClaimRouter). `mintedAt` is kept as a stats field only — no eligibility, the
// accumulator/feeDebt couple makes JIT impossible by construction.

import { ERC20 } from "solady/src/tokens/ERC20.sol";
import { Ownable } from "solady/src/auth/Ownable.sol";
import { ReentrancyGuard } from "solady/src/utils/ReentrancyGuard.sol";

import { KaleidoMirror } from "./KaleidoMirror.sol";

// The protocol pool is Uniswap v3 (VEG2-1150): DexScreener indexes v2/v3 on Robinhood Chain but not v4, and the
// chart is the acquisition funnel — so KALEIDO's OWN pool launches on v3 (native chart day 1). The claim swaps
// (ClaimRouter/V4SwapLib) stay on v4 — the protocol is bi-venue. Minimal v3 surfaces, vendored inline (no new
// submodule): the NonfungiblePositionManager (create/mint the position, collect fees) and WETH9 (unwrap fees).

interface INonfungiblePositionManager {
    function createAndInitializePoolIfNecessary(address token0, address token1, uint24 fee, uint160 sqrtPriceX96)
        external
        payable
        returns (address pool);

    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    function mint(MintParams calldata params)
        external
        payable
        returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);
}

interface IWETH9 {
    function withdraw(uint256) external;
}

/// @dev v3 factory + pool slot0 — used ONLY in `seed` to prove the pool the NPM returned is the canonical one and
///      that it was initialized at exactly the launch price (H-01: the token address is predictable, so an attacker
///      can front-run the pool init). Never touched on a hot path.
interface IUniswapV3Factory {
    function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address);
}

interface IUniswapV3PoolSlot0 {
    function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16, uint16, uint16, uint8, bool);
}

/// @dev External, replaceable metadata renderer for the Shares (VEG2-1129/§4). Only the POINTER is part of the
///      frozen ABI; the art contract is a separate, post-freeze ticket and can be re-pointed. Called from views
///      only (see nftTokenURI / nftContractURI), never from a money or transfer path.
interface IKaleidoRenderer {
    function tokenURI(uint256 tokenId) external view returns (string memory);
    function contractURI() external view returns (string memory);
}

contract KaleidoToken is ERC20, Ownable, ReentrancyGuard {
    /*                            ERRORS                          */

    error AlreadySeeded();
    error ZeroAddress();
    error NotOwnerOrApproved();
    error InvalidTokenId();
    error MirrorOnly();
    error TransferToZero();
    error ExcludedShareRecipient();
    error SelfTransferDisallowed();
    error EthTransferFailed();
    error ClaimRouterOnly();
    error ClaimRouterAlreadySet();
    error NotShareOwner();
    error SeedPoolMismatch();
    error SeedPriceMismatch();
    error SeedNotSingleSided();
    error RouterHandshakeFailed();
    error NotDev();

    /*                            EVENTS                          */

    event Seeded(uint256 indexed posmTokenId, uint160 sqrtPriceX96, uint128 liquidity);
    event NFTMinted(address indexed to, uint256 indexed tokenId);
    event NFTBurned(address indexed from, uint256 indexed tokenId);
    event FeesHarvested(uint256 ethToHolders, uint256 ethToDev, uint256 bounty, uint256 tokenBurned);
    event ClaimRouterSet(address indexed router);
    event FeesSettled(address indexed holder, uint256 ethOut, uint256 tokenCount);
    /// @dev `amount` reports the GROSS forfeited pending of the burned Share (its pre-floor accrued dividend), not
    /// the net the survivors ultimately claim — the sub-wei rounding remainder stays custodied as invariant dust
    /// and is never re-emitted (the net is anyway unknowable at burn: it depends on the survivors' own claim floors).
    event Forfeited(uint256 indexed tokenId, uint256 amount);
    event ForfeitsSwept(uint256 ethToHolders, uint256 bounty);
    event RendererSet(address indexed renderer);
    event DevWithdrawn(address indexed to, uint256 amount);

    /*                           CONSTANTS                        */

    /// @dev Token metadata constants — centralized (final brand, VEG2-1148).
    string internal constant NAME = "Kaleido";
    string internal constant SYMBOL = "KALEIDO";

    uint256 public constant SUPPLY = 5000 ether;
    uint256 public constant UNIT = 1 ether;
    uint24 public constant POOL_FEE = 10_000; // 1% static
    int24 public constant TICK_SPACING = 200;

    /// @dev Per-share ETH fee accumulator scale (Prism-vanilla). 1e12 keeps per-share amounts in range.
    uint256 internal constant ACC_SCALE = 1e12;
    /// @dev pokeFees caller bounty = 0.25% of the ETH harvested, capped at BOUNTY_CAP (documented absolute cap —
    /// poking is cheap-to-incentivize, not a yield source; the cap stops a big accrued harvest over-paying one poker).
    uint256 internal constant BOUNTY_BPS = 25;
    uint256 internal constant BOUNTY_CAP = 0.05 ether;
    /// @dev Of the post-bounty ETH: 90% to holders (accEthPerShare); the remaining 10% is accrued to `devOwed`.
    uint256 internal constant HOLDER_BPS = 9000;
    /// @dev H-01: max KALEIDO left in the reserve after the single-sided mint before `seed` reverts. A real fresh
    /// mainnet-4663 mint strands just 4 wei of rounding (measured, `test_Fork_V3_SeedSingleSided`); this 1 gwei
    /// tolerance is ~2.5e8x that headroom (safe across launch-tick variance) yet still reverts any material partial
    /// fill — the reserve can never be recovered (seed is one-shot). See docs/security-notes.md §7.
    uint256 internal constant SEED_MAX_DUST = 1e9;

    /// @dev Transient slot for the realignment-skip flag.
    uint256 private constant _SILENT_SLOT = 0x9a8f8d1e3c5b7a9d6e2f4a8c7b5d3f9e1a6c4b8d2e7f5a9c3b1d6e8f2a4c7b9d;

    /*                          IMMUTABLES                        */

    /// @dev Uniswap v3 NonfungiblePositionManager — creates/holds the KALEIDO/WETH position and collects its fees.
    address public immutable npm;
    /// @dev Canonical wrapped native — the pool's counter-asset (v3 has no native ETH); fees in it are unwrapped.
    address public immutable weth;
    /// @dev Uniswap v3 factory (reference; the pool is created via the NPM). Exposed for the frontend/explorer.
    address public immutable v3Factory;
    /// @dev Fixed token ordering vs WETH (v3 pools sort by address). Decides which side of a collect is WETH.
    bool public immutable kaleidoIsToken0;
    KaleidoMirror public immutable mirror;

    /// @dev Recipient of the 10% dev share of harvested ETH fees. Immutable — no admin path can redirect fees.
    address public immutable dev;

    /*                        PACKED STORAGE                      */

    bool public seeded;
    uint256 public positionTokenId;
    int24 public globalTickLower;
    int24 public globalTickUpper;

    /// @dev The v3 KALEIDO/WETH pool — set in `seed`. Excluded from Share accounting in `_afterTokenTransfer`
    /// (it is the reserve holder, like the v4 PoolManager was). address(0) until seeded.
    address public pool;

    /// @dev Monotonic ERC-721 id counter. First minted id is 1.
    uint256 private _nextTokenId;

    /// @dev Live NFT count = share denominator.
    uint256 public totalShares;

    /// @dev Packed owner + ownedIndex: [0..159]=owner, [160..191]=ownedIndex.
    mapping(uint256 => uint256) private _oo;
    /// @dev Per-user packed owned tokenIds, 8 uint32 per slot.
    mapping(address => mapping(uint256 => uint256)) private _ownedSlots;
    /// @dev Packed per-user metadata: [0..31]=ownedLength.
    mapping(address => uint256) private _addressData;

    mapping(uint256 => address) private _nftApprovals;
    mapping(address => mapping(address => bool)) private _nftOperatorApprovals;

    /// @dev Acquisition timestamp of each live tokenId — stats/provenance only (v1.1 dropped vote/claim
    /// eligibility; the accumulator/feeDebt couple makes JIT impossible by construction). Set on mint + move.
    mapping(uint256 => uint256) public mintedAt;

    /// @dev Cumulative ETH-per-live-Share, scaled by ACC_SCALE (Prism-vanilla). Monotonic; credited in pokeFees.
    uint256 public accEthPerShare;
    /// @dev Per-tokenId reward debt (snapshot of accEthPerShare at mint). Claimable = accEthPerShare - feeDebt
    /// (read by ClaimRouter, VEG2-1125). Setting it at mint stops a fresh Share from claiming past accrual (JIT).
    mapping(uint256 => uint256) public feeDebt;

    /// @dev The ClaimRouter (VEG2-1125) — the only address allowed to settle pending fees and pull the backing
    /// ETH. Wired one-shot post-deploy (core and router reference each other). address(0) until set.
    address public claimRouter;

    /// @dev Parked forfeit pot (VEG2-1126): unclaimed pending ETH of Shares that burned with NO survivor to
    /// inherit it (the last Share out). Normal burns credit survivors inline in `_burnNFT` (no window to game);
    /// only this degenerate case parks here, awaiting `sweepForfeits` once a Share returns — which dodges a
    /// divide-by-zero. The v1.1 "forfeited" term of the master invariant.
    uint256 public forfeitedWei;

    /// @dev H-02: the 10% dev fee share is ACCRUED here (not pushed) so a dev address that rejects ETH can never
    /// block a harvest. `dev` pulls it via `withdrawDev`. A backed liability — part of the master conservation
    /// (core ETH == holder-owed + devOwed + dust); cleared on withdraw.
    uint256 public devOwed;

    /// @dev Replaceable metadata renderer (VEG2-1129). address(0) => the Shares report an empty tokenURI (the
    /// pre-renderer default). Owner-settable and RE-settable (SPEC §4 "upgradeable"); read from views only.
    address public renderer;

    /*                        PACKING HELPERS                     */

    function _ownerOf(uint256 id) internal view returns (address) {
        return address(uint160(_oo[id]));
    }

    function _ownedIndexOf(uint256 id) internal view returns (uint32) {
        return uint32(_oo[id] >> 160);
    }

    function _setOo(uint256 id, address owner_, uint32 index) internal {
        _oo[id] = uint256(uint160(owner_)) | (uint256(index) << 160);
    }

    function _ownedGet(address user, uint256 index) internal view returns (uint32) {
        return uint32(_ownedSlots[user][index >> 3] >> ((index & 7) << 5));
    }

    function _ownedSet(address user, uint256 index, uint32 value) internal {
        uint256 slotIdx = index >> 3;
        uint256 shift = (index & 7) << 5;
        uint256 word = _ownedSlots[user][slotIdx];
        _ownedSlots[user][slotIdx] = (word & ~(uint256(0xFFFFFFFF) << shift)) | (uint256(value) << shift);
    }

    function _ownedLength(address user) internal view returns (uint32) {
        return uint32(_addressData[user]);
    }

    function _setOwnedLength(address user, uint32 len) internal {
        uint256 v = _addressData[user];
        _addressData[user] = (v & ~uint256(0xFFFFFFFF)) | uint256(len);
    }

    /*                         CONSTRUCTOR                        */

    constructor(address _v3Factory, address _npm, address _weth, address _owner, address _dev) {
        if (
            _v3Factory == address(0) || _npm == address(0) || _weth == address(0) || _owner == address(0)
                || _dev == address(0)
        ) {
            revert ZeroAddress();
        }
        v3Factory = _v3Factory;
        npm = _npm;
        weth = _weth;
        dev = _dev;
        // v3 pools sort tokens by address; fix which side of the pair (and of a fee collect) is KALEIDO.
        kaleidoIsToken0 = address(this) < _weth;

        mirror = new KaleidoMirror(address(this));

        _initializeOwner(_owner);

        // §12 = 100% pool: the full supply is minted to the contract ("the pool is the reserve").
        _mint(address(this), SUPPLY);
    }

    /*                        ERC20 METADATA                      */

    function name() public pure override returns (string memory) {
        return NAME;
    }

    function symbol() public pure override returns (string memory) {
        return SYMBOL;
    }

    /// @notice L-02: KALEIDO does NOT use Permit2 (its pool is v3 via the NPM; claims settle in native ETH). Opt out
    ///         of Solady's default infinite Permit2 allowance so `allowance(holder, Permit2)` is a real 0 and no
    ///         Permit2 compromise can move a holder's balance without an explicit approval.
    function _givePermit2InfiniteAllowance() internal pure override returns (bool) {
        return false;
    }

    /*                             SEED                           */

    /// @notice One-time: create+initialize the KALEIDO/WETH v3 pool and mint the single-sided protocol position
    ///         (100% KALEIDO, "the pool is the reserve"). The caller supplies the geometry so the current price
    ///         sits at the KALEIDO-heavy boundary of `[tickLower, tickUpper]` — so all SUPPLY enters as KALEIDO and
    ///         no WETH is needed. The position NFT is minted to and permanently held by this contract; there is NO
    ///         path that decreases/burns/transfers it, so protocol liquidity can never be withdrawn.
    function seed(uint160 sqrtPriceX96, int24 tickLower, int24 tickUpper) external onlyOwner returns (uint256 tokenId) {
        if (seeded) revert AlreadySeeded();
        seeded = true;
        globalTickLower = tickLower;
        globalTickUpper = tickUpper;

        (address token0, address token1) = kaleidoIsToken0 ? (address(this), weth) : (weth, address(this));

        // let the NPM pull the single-sided KALEIDO from the reserve (self-allowance, one-shot amount).
        // ⚠️ INVARIANT (load-bearing): the contract NEVER approves WETH to the NPM. So the mint can only spend
        // KALEIDO — if the geometry ever implied a WETH leg, the mint reverts (retryable, seeded rolls back)
        // instead of pulling WETH. This is what enforces single-sidedness by construction. Do not add a WETH
        // approval here.
        _approve(address(this), npm, SUPPLY);

        pool =
            INonfungiblePositionManager(npm).createAndInitializePoolIfNecessary(token0, token1, POOL_FEE, sqrtPriceX96);

        // H-01: the token (hence pool) address is predictable, so an attacker can pre-create + initialize the
        // canonical pool before this runs. createAndInitializePoolIfNecessary leaves an ALREADY-initialized pool at
        // its EXISTING price and returns it unchanged. So prove (a) it is the factory's canonical pool for our key,
        // and (b) it sits at EXACTLY the launch price we supplied. A mismatch means the init was front-run — revert
        // rather than launch the market at the attacker's price (seeded rolls back; redeploy at a fresh address).
        if (pool != IUniswapV3Factory(v3Factory).getPool(token0, token1, POOL_FEE)) revert SeedPoolMismatch();
        (uint160 poolPrice,,,,,,) = IUniswapV3PoolSlot0(pool).slot0();
        if (poolPrice != sqrtPriceX96) revert SeedPriceMismatch();

        uint128 liquidity;
        (liquidity, tokenId) = _seedMint(token0, token1, tickLower, tickUpper);
        positionTokenId = tokenId;

        // revoke the residual self-allowance so nothing can ever pull the leftover reserve KALEIDO to the NPM.
        _approve(address(this), npm, 0);

        emit Seeded(tokenId, sqrtPriceX96, liquidity);
    }

    /// @dev The single-sided mint + its H-01 result checks, split out of `seed` to keep the stack shallow. Reverts
    ///      unless the mint locked nonzero liquidity, pulled NO WETH, and consumed all but <= SEED_MAX_DUST of the
    ///      reserve KALEIDO (no gross partial fill — seed is one-shot, so any stranded reserve is permanent).
    function _seedMint(address token0, address token1, int24 tickLower, int24 tickUpper)
        private
        returns (uint128 liquidity, uint256 tokenId)
    {
        // single-sided: SUPPLY on the KALEIDO side, 0 on the WETH side. Price is pinned in `seed` → no slippage.
        (uint256 amount0Desired, uint256 amount1Desired) = kaleidoIsToken0 ? (SUPPLY, uint256(0)) : (uint256(0), SUPPLY);
        INonfungiblePositionManager.MintParams memory mp = INonfungiblePositionManager.MintParams({
            token0: token0,
            token1: token1,
            fee: POOL_FEE,
            tickLower: tickLower,
            tickUpper: tickUpper,
            amount0Desired: amount0Desired,
            amount1Desired: amount1Desired,
            amount0Min: 0,
            amount1Min: 0,
            recipient: address(this),
            deadline: block.timestamp + 60
        });
        uint256 amount0;
        uint256 amount1;
        (tokenId, liquidity, amount0, amount1) = INonfungiblePositionManager(npm).mint(mp);

        // H-01: prove the mint locked a real, single-sided, (almost) whole reserve — nonzero liquidity, NO WETH leg
        // pulled, and no gross partial fill leaving reserve permanently stranded. SEED_MAX_DUST is the rounding
        // tolerance; a real fresh mint strands only a few wei (fork-calibrated, see docs/security-notes.md).
        uint256 wethUsed = kaleidoIsToken0 ? amount1 : amount0;
        uint256 kaleidoUsed = kaleidoIsToken0 ? amount0 : amount1;
        if (liquidity == 0 || wethUsed != 0 || SUPPLY - kaleidoUsed > SEED_MAX_DUST) revert SeedNotSingleSided();
    }

    /*                       FEE HARVEST                          */

    /// @notice Permissionless fee harvest (money path #1). Collects the v3 position's accrued LP fees (both
    ///         tokens; `collect` touches fees only, never liquidity), unwraps the WETH side to ETH, then — SATO
    ///         rule (invariant #2), on MEASURED deltas only — pays the caller a capped 0.25% bounty, credits 90%
    ///         of the rest to `accEthPerShare`, ACCRUES 10% to `devOwed` (H-02: pull, not push — `dev` withdraws via
    ///         `withdrawDev`), and burns 100% of the KALEIDO-side fees. Clean no-op when nothing accrued (never
    ///         reverts, never a phantom bounty). CEI: all accounting effects precede the single ETH send (the
    ///         bounty); `nonReentrant`.
    function pokeFees() external nonReentrant {
        if (!seeded || totalShares == 0) return;

        uint256 ethBefore = address(this).balance;
        uint256 kaleidoBefore = balanceOf(address(this));

        // v3 fee collection: pull ALL owed fees of the position here (both tokens). Fees only — no liquidity moves.
        INonfungiblePositionManager.CollectParams memory cp = INonfungiblePositionManager.CollectParams({
            tokenId: positionTokenId,
            recipient: address(this),
            amount0Max: type(uint128).max,
            amount1Max: type(uint128).max
        });
        (uint256 amount0, uint256 amount1) = INonfungiblePositionManager(npm).collect(cp);

        // Unwrap ONLY the WETH this collect returned (SATO: never unwrap/credit WETH that was merely donated to
        // the contract). withdraw() sends ETH to receive() (empty) — nonReentrant + measuring AFTER the unwrap
        // means the ETH is on-balance and can't be double-counted.
        uint256 wethCollected = kaleidoIsToken0 ? amount1 : amount0;
        if (wethCollected > 0) IWETH9(weth).withdraw(wethCollected);

        // SATO rule: credit ONLY measured deltas. Donations (raw ETH already held, WETH beyond `wethCollected`,
        // or KALEIDO) sit outside these deltas and are never credited.
        uint256 ethGained = address(this).balance - ethBefore;
        uint256 tokenBurned = balanceOf(address(this)) - kaleidoBefore;

        uint256 bounty;
        uint256 toHolders;
        uint256 toDev;
        // effects
        if (ethGained > 0) {
            bounty = bountyFor(ethGained);
            uint256 net = ethGained - bounty;
            toHolders = net * HOLDER_BPS / 10_000;
            toDev = net - toHolders;
            accEthPerShare += toHolders * ACC_SCALE / totalShares;
            // H-02: ACCRUE the dev share (pull, not push) so a dev address that rejects ETH can never brick a
            // harvest. dev withdraws via withdrawDev; the wei stays in the core backing devOwed.
            devOwed += toDev;
        }
        if (tokenBurned > 0) _burn(address(this), tokenBurned);
        // interaction (after all effects): ONLY the bounty is pushed — to msg.sender, who by calling accepts it, so
        // a poker that rejects ETH fails only its own tx, never the system. The dev share is pull-based (above).
        if (bounty > 0) _sendEth(msg.sender, bounty);

        if (ethGained > 0 || tokenBurned > 0) emit FeesHarvested(toHolders, toDev, bounty, tokenBurned);
    }

    /// @notice The poke bounty for a given ETH harvest: 0.25%, capped at BOUNTY_CAP. Pure — also safe for UIs.
    function bountyFor(uint256 ethGained) public pure returns (uint256 b) {
        b = ethGained * BOUNTY_BPS / 10_000;
        if (b > BOUNTY_CAP) b = BOUNTY_CAP;
    }

    /// @notice Permissionless (money path #2, VEG2-1126): drain the PARKED forfeit pot into `accEthPerShare` for
    ///         the current survivors, minus a capped 0.25% keeper bounty (same policy/dust as pokeFees). The pot is
    ///         only ever non-zero from the degenerate case where the LAST Share burned with pending owed (normal
    ///         burns credit survivors inline in `_burnNFT`, leaving no JIT window). It therefore parks until a
    ///         Share exists again; the first returning holder(s) receive this orphaned ETH — acceptable, as by
    ///         definition there were no survivors to owe it to. Clean no-op when the pot is empty OR no Shares are
    ///         live. CEI: pot cleared + accumulator credited before the bounty send; `nonReentrant`.
    function sweepForfeits() external nonReentrant {
        uint256 amount = forfeitedWei;
        if (amount == 0 || totalShares == 0) return;
        forfeitedWei = 0; // effect
        uint256 bounty = bountyFor(amount);
        uint256 toHolders = amount - bounty;
        accEthPerShare += toHolders * ACC_SCALE / totalShares; // effect
        if (bounty > 0) _sendEth(msg.sender, bounty); // interaction
        emit ForfeitsSwept(toHolders, bounty);
    }

    /// @notice H-02: `dev` pulls its accrued 10% fee share to a recipient of its choice. Pull, not push, so a
    ///         rejecting dev address can never brick the harvest path; `dev`-only, CEI, nonReentrant.
    function withdrawDev(address to) external nonReentrant returns (uint256 amount) {
        if (msg.sender != dev) revert NotDev();
        if (to == address(0)) revert ZeroAddress();
        amount = devOwed;
        devOwed = 0; // effect before interaction (CEI)
        if (amount > 0) {
            emit DevWithdrawn(to, amount);
            _sendEth(to, amount); // interaction
        }
    }

    function _sendEth(address to, uint256 amount) private {
        (bool ok,) = to.call{ value: amount }("");
        if (!ok) revert EthTransferFailed();
    }

    /*                  CLAIM SETTLEMENT (VEG2-1125)             */

    /// @notice One-shot wiring of the ClaimRouter. The core and the router reference each other, so the router
    ///         is deployed second and set here. Owner-only and immutable-after-set — once live, no admin path can
    ///         redirect where settled fees flow.
    function setClaimRouter(address router) external onlyOwner {
        if (router == address(0)) revert ZeroAddress();
        if (claimRouter != address(0)) revert ClaimRouterAlreadySet();
        // M-03: the one-shot is irreversible and a bad value permanently disables every claim, so prove the router
        // is a real contract that points BACK to this core — not an EOA, a wrong-chain copy, or a mismatched
        // instance. Low-level staticcall (not a typed call) so a fallback returning malformed/short/empty data is
        // rejected CLEANLY with RouterHandshakeFailed rather than bubbling an opaque ABI-decode revert; require the
        // return to be exactly 32 bytes decoding to this core. A missing/reverting getter fails the same way.
        if (router.code.length == 0) revert RouterHandshakeFailed();
        (bool ok, bytes memory ret) = router.staticcall(abi.encodeWithSignature("core()"));
        if (!ok || ret.length != 32 || abi.decode(ret, (address)) != address(this)) revert RouterHandshakeFailed();
        claimRouter = router;
        emit ClaimRouterSet(router);
    }

    /// @notice Set (or clear, with address(0)) the Shares' metadata renderer. Owner-only and intentionally
    ///         RE-settable — SPEC §4 keeps the art "upgradeable"; the renderer is external and its output is
    ///         degraded-safe (see nftTokenURI). This is the owner's ONLY residual power once `seed` +
    ///         `setClaimRouter` are done, and it is purely cosmetic — it can never touch accrual, claims, the
    ///         registry, or the backing ETH. Renouncing ownership afterwards permanently freezes the art pointer.
    function setRenderer(address newRenderer) external onlyOwner {
        renderer = newRenderer;
        emit RendererSet(newRenderer);
    }

    /// @notice Pending ETH claimable for a single Share (tokenId), in wei: (accEthPerShare − feeDebt) de-scaled.
    ///         Each NFT is exactly one Share, so there is no per-holder share count. Returns 0 for a
    ///         burned/never-minted id (its feeDebt is meaningless without a live owner). This is the exact amount
    ///         `settleClaim` pays; UIs aggregate it across a wallet's tokenIds.
    function pendingFees(uint256 tokenId) public view returns (uint256) {
        if (_ownerOf(tokenId) == address(0)) return 0;
        uint256 debt = feeDebt[tokenId];
        uint256 acc = accEthPerShare;
        return acc > debt ? (acc - debt) / ACC_SCALE : 0;
    }

    /// @notice ClaimRouter-only. Settle the pending ETH of `tokenIds` (each MUST be owned by `holder`), snapshot
    ///         their feeDebt up to accEthPerShare, and forward the aggregate ETH to the router. CEI: every
    ///         feeDebt write (effect) precedes the single ETH send (interaction); `nonReentrant`. A duplicate id
    ///         in the array pays once (the second sighting sees debt == acc → adds 0), so double-claim is
    ///         impossible. Reverts atomically if any id is not `holder`'s — a failed claim never loses pending.
    ///         The router turns this ETH into the holder's chosen dividend (ETH via `claim`, a stock via
    ///         `claimInto`); the core itself never touches a stock token.
    function settleClaim(uint256[] calldata tokenIds, address holder) external nonReentrant returns (uint256 ethOut) {
        if (msg.sender != claimRouter) revert ClaimRouterOnly();
        uint256 acc = accEthPerShare;
        uint256 n = tokenIds.length;
        for (uint256 i; i < n; ++i) {
            uint256 id = tokenIds[i];
            if (_ownerOf(id) != holder) revert NotShareOwner();
            uint256 debt = feeDebt[id];
            if (acc > debt) {
                unchecked {
                    ethOut += (acc - debt) / ACC_SCALE;
                }
                feeDebt[id] = acc; // effect BEFORE the interaction (CEI)
            }
        }
        if (ethOut > 0) {
            emit FeesSettled(holder, ethOut, n);
            _sendEth(claimRouter, ethOut); // interaction
        }
    }

    /*                      ERC20 → NFT SYNC                      */

    function _afterTokenTransfer(address from, address to, uint256) internal override {
        if (_isSilent()) return;

        // The v3 pool is the reserve holder (KALEIDO flows core→pool at seed, pool→buyer on a buy, pool→core on a
        // fee collect) — exclude it from Share accounting, exactly as the v4 PoolManager was excluded. `pool` is 0
        // until seeded, which is harmless (no transfer references it before then).
        address p = pool;
        address self = address(this);

        bool fromIsUser = from != address(0) && from != p && from != self;
        bool toIsUser = to != address(0) && to != p && to != self;

        if (fromIsUser && toIsUser) _realignPair(from, to);
        else if (fromIsUser) _realignSolo(from);
        else if (toIsUser) _realignSolo(to);
    }

    function _realignPair(address from, address to) private {
        uint256 fromTarget = balanceOf(from) / UNIT;
        uint256 toTarget = balanceOf(to) / UNIT;
        uint256 fromCur = _ownedLength(from);
        uint256 toCur = _ownedLength(to);

        uint256 fromLoses = fromCur > fromTarget ? fromCur - fromTarget : 0;
        uint256 toGains = toTarget > toCur ? toTarget - toCur : 0;

        uint256 transferable = fromLoses < toGains ? fromLoses : toGains;
        uint256 toBurn = fromLoses - transferable;
        uint256 toMint = toGains - transferable;

        for (uint256 i = 0; i < transferable; i++) {
            uint256 tokenId = _pickTail(from);
            _move(from, to, tokenId);
        }
        for (uint256 i = 0; i < toBurn; i++) {
            uint256 tokenId = _pickTail(from);
            _burnNFT(from, tokenId);
        }
        for (uint256 i = 0; i < toMint; i++) {
            _mintNFT(to);
        }
    }

    function _realignSolo(address user) private {
        uint256 target = balanceOf(user) / UNIT;
        uint256 cur = _ownedLength(user);

        if (target > cur) {
            uint256 toMint = target - cur;
            for (uint256 i = 0; i < toMint; i++) {
                _mintNFT(user);
            }
        } else if (cur > target) {
            uint256 toBurn = cur - target;
            for (uint256 i = 0; i < toBurn; i++) {
                uint256 tokenId = _pickTail(user);
                _burnNFT(user, tokenId);
            }
        }
    }

    /*                      NFT INTERNAL OPS                      */

    function _mintNFT(address to) private {
        unchecked {
            uint256 tokenId = ++_nextTokenId;
            uint32 index = _ownedLength(to);
            _setOo(tokenId, to, index);
            // forge-lint: disable-next-line(unsafe-typecast) — tokenId is bounded (DN404 uint32 id packing).
            _ownedSet(to, index, uint32(tokenId));
            _setOwnedLength(to, index + 1);
            mintedAt[tokenId] = block.timestamp;
            feeDebt[tokenId] = accEthPerShare;
            totalShares = totalShares + 1;
            mirror.emitTransfer(address(0), to, tokenId);
            emit NFTMinted(to, tokenId);
        }
    }

    function _burnNFT(address from, uint256 tokenId) private {
        uint32 index = _ownedIndexOf(tokenId);
        uint32 lastIdx = _ownedLength(from) - 1;

        if (index != lastIdx) {
            uint32 lastTokenId = _ownedGet(from, lastIdx);
            _ownedSet(from, index, lastTokenId);
            _setOo(lastTokenId, from, index);
        }
        _setOwnedLength(from, lastIdx);

        delete _oo[tokenId];
        delete _nftApprovals[tokenId];
        delete mintedAt[tokenId];

        // v1.1 forfeiture (VEG2-1126): the burned Share's unclaimed pending (its accrued dividend, already ETH in
        // the contract) is redistributed to the SURVIVORS. Credit accEthPerShare INLINE over the survivor count
        // AFTER this burn, so the forfeit lands on exactly the Shares live at the burn instant — no window for a
        // Share minted afterwards to capture it (this is what keeps "JIT impossible by construction" true; a lazy
        // park-then-sweep let an attacker mint between burn and sweep and dilute the honest survivors). O(1),
        // accounting-only, no external call, no loop → a burn can never brick a transfer (invariant #5). feeDebt
        // is dropped so the same pending can never ALSO be claimed (burned ids fail settleClaim's owner check
        // anyway). Only when the LAST Share burns (no survivors) do we PARK the amount in forfeitedWei —
        // sweepForfeits redistributes that once a Share exists again, dodging a divide-by-zero here.
        uint256 acc = accEthPerShare;
        uint256 debt = feeDebt[tokenId];
        delete feeDebt[tokenId];

        unchecked {
            totalShares = totalShares - 1;
        }

        if (acc > debt) {
            uint256 forfeitScaled = acc - debt;
            if (totalShares > 0) {
                accEthPerShare = acc + forfeitScaled / totalShares; // survivors inherit, atomically
            } else {
                forfeitedWei += forfeitScaled / ACC_SCALE; // last Share out — park until one returns
            }
            uint256 forfeitWei = forfeitScaled / ACC_SCALE;
            if (forfeitWei > 0) emit Forfeited(tokenId, forfeitWei);
        }

        mirror.emitTransfer(from, address(0), tokenId);
        emit NFTBurned(from, tokenId);
    }

    function _move(address from, address to, uint256 tokenId) private {
        uint32 fromIdx = _ownedIndexOf(tokenId);
        uint32 lastIdx = _ownedLength(from) - 1;

        if (fromIdx != lastIdx) {
            uint32 lastTokenId = _ownedGet(from, lastIdx);
            _ownedSet(from, fromIdx, lastTokenId);
            _setOo(lastTokenId, from, fromIdx);
        }
        _setOwnedLength(from, lastIdx);

        uint32 toIdx = _ownedLength(to);
        // forge-lint: disable-next-line(unsafe-typecast) — tokenId is bounded (DN404 uint32 id packing).
        _ownedSet(to, toIdx, uint32(tokenId));
        _setOo(tokenId, to, toIdx);
        _setOwnedLength(to, toIdx + 1);

        // stats/provenance: mintedAt tracks the current holder's acquisition time (v1.1 — no eligibility use).
        mintedAt[tokenId] = block.timestamp;

        delete _nftApprovals[tokenId];
        mirror.emitTransfer(from, to, tokenId);
    }

    function _pickTail(address user) private view returns (uint256) {
        return _ownedGet(user, _ownedLength(user) - 1);
    }

    /*                      MIRROR CALLBACKS                      */

    modifier onlyMirror() {
        if (msg.sender != address(mirror)) revert MirrorOnly();
        _;
    }

    function handleNFTTransfer(address from, address to, uint256 tokenId, address caller)
        external
        onlyMirror
        nonReentrant
    {
        if (to == address(0)) revert TransferToZero();
        // The core reserve and canonical pool are deliberately excluded from Share accounting in the ERC-20 hook.
        // The mirror path runs its paired KALEIDO transfer under `_SILENT`, so allowing either destination here
        // would bypass that exclusion: the excluded address would retain a live Share while a later pool/core
        // outflow could mint another one. Reject before `_move` so tokenId, feeDebt and ownership remain unchanged.
        if (to == address(this) || to == pool) revert ExcludedShareRecipient();
        if (from == to) revert SelfTransferDisallowed();

        address owner_ = _ownerOf(tokenId);
        if (owner_ == address(0) || owner_ != from) revert InvalidTokenId();
        if (caller != owner_ && _nftApprovals[tokenId] != caller && !_nftOperatorApprovals[owner_][caller]) {
            revert NotOwnerOrApproved();
        }

        _move(from, to, tokenId);

        _setSilent(true);
        _transfer(from, to, UNIT);
        _setSilent(false);
    }

    function handleNFTApprove(address spender, uint256 tokenId, address caller) external onlyMirror {
        address owner_ = _ownerOf(tokenId);
        if (owner_ == address(0)) revert InvalidTokenId();
        if (caller != owner_ && !_nftOperatorApprovals[owner_][caller]) revert NotOwnerOrApproved();
        _nftApprovals[tokenId] = spender;
        mirror.emitApproval(owner_, spender, tokenId);
    }

    function handleNFTSetApprovalForAll(address operator, bool approved, address caller) external onlyMirror {
        _nftOperatorApprovals[caller][operator] = approved;
        mirror.emitApprovalForAll(caller, operator, approved);
    }

    /*                          NFT VIEWS                         */

    function nftOwnerOf(uint256 tokenId) external view returns (address o) {
        o = _ownerOf(tokenId);
        if (o == address(0)) revert InvalidTokenId();
    }

    function nftBalanceOf(address owner_) external view returns (uint256) {
        return _ownedLength(owner_);
    }

    function nftGetApproved(uint256 tokenId) external view returns (address) {
        return _nftApprovals[tokenId];
    }

    function nftIsApprovedForAll(address o, address s) external view returns (bool) {
        return _nftOperatorApprovals[o][s];
    }

    /// @notice ERC-721 tokenURI for a Share, via the replaceable `renderer` (VEG2-1129). Reverts only for a
    ///         non-existent id (ERC-721 requirement). With no renderer set it returns "" (the pre-renderer
    ///         default); with one set it forwards, but a reverting/gas-heavy renderer degrades to "" — a bad or
    ///         malicious renderer can never brick marketplace metadata. View-only: on no money or transfer path.
    function nftTokenURI(uint256 tokenId) external view returns (string memory) {
        if (_ownerOf(tokenId) == address(0)) revert InvalidTokenId();
        address r = renderer;
        if (r == address(0)) return "";
        try IKaleidoRenderer(r).tokenURI(tokenId) returns (string memory uri) {
            return uri;
        } catch {
            return "";
        }
    }

    /// @notice ERC-7572 collection metadata, via the replaceable `renderer`. Same degrade-to-"" safety as
    ///         nftTokenURI. Surfaced on the mirror as `contractURI()`.
    function nftContractURI() external view returns (string memory) {
        address r = renderer;
        if (r == address(0)) return "";
        try IKaleidoRenderer(r).contractURI() returns (string memory uri) {
            return uri;
        } catch {
            return "";
        }
    }

    function ownedTokensOf(address owner_) external view returns (uint256[] memory ids) {
        uint256 n = _ownedLength(owner_);
        ids = new uint256[](n);
        for (uint256 i = 0; i < n; i++) {
            ids[i] = uint256(_ownedGet(owner_, i));
        }
    }

    /*                         SILENT FLAG                        */

    function _setSilent(bool v) private {
        assembly {
            tstore(_SILENT_SLOT, v)
        }
    }

    function _isSilent() private view returns (bool v) {
        assembly {
            v := tload(_SILENT_SLOT)
        }
    }

    /// @dev Accept the v3 position NFT if the NonfungiblePositionManager ever safe-mints it to us (current v3
    ///      mints via plain _mint, but this keeps `seed` robust across NPM variants). Not an ERC-721 itself.
    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        return this.onERC721Received.selector;
    }

    receive() external payable { }
}
Chain explorer6164msChain node78ms