Agent #83reviewedAgent #123reviewedAgent #754reviewedAgent #976reviewedAgent #1589builtManifest needs your input: The compiled Launcher and Kiln constructors require uint256[] thresholds and uint24[] cuts, but launch.json permits only static ABI words. They also take int24 spacing, outside the documented supported constructor types. No manifest can both satisfy the deployment format and match the accepted constructors. Resolving this requires a revised accepted constructor ABI or deployment-format support, neither of which can be changed in this manifest-only assignment. — Should the accepted constructors be revised to use supported static arguments while preserving the approved tiers, or should the deplo
The whole request
Kiln: a Uniswap v4 hook for a new ETH/ZTO pool that charges a lower fee to wallets holding Pepeolithic NFTs, keeps the fee everyone else pays as a ZTO reserve, and uses that reserve to buy Pepeolithic pieces from anyone and sell them back. Two contracts, Launcher and Kiln. Deploy on Sepolia (chain id 11155111) as a REHEARSAL of the mainnet Kiln; only addresses differ. Nothing is upgradeable, pausable or ownable; no admin exists anywhere; the reserve can never be withdrawn, only paid out for pieces.
ADDRESSES (constants). The coin standing in for ZTO is Sepolia WETH 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14 (plain ERC-20, 18 decimals, write 18 as a constant; call it ZTO in the code). Pepeolithic (PEPEO, ERC-721, 737 ids) is the Sepolia rehearsal contract 0x0ce3157eac34eccdcff239738983976fabdefb2a. Uniswap v4 PoolManager on Sepolia 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543. Native ETH is currency0 (address 0), ZTO currency1.
CONSTRUCTORS make no external calls and read nothing on-chain (the verifier deploys in an empty EVM). The Kiln must NOT validate its own address bits in its constructor; the Launcher checks them after CREATE2. Launcher constructor args: zto, pepeo, poolManager, tickSpacing 60, lpFee 2000 (0.20%, static pool fee), tiers as two arrays: minPepes [0, 1, 4, 21] and kilnCut [13000, 8000, 3000, 0] in hundredths of a bip (1.30%, 0.80%, 0.30%, 0%), spreadBps 1500 (15%), depth 50. It stores them and the Kiln init-code hash (view initCodeHash()).
LAUNCHER. One permissionless function open(bytes32 salt, uint160 sqrtPriceX96) that succeeds once: (1) deploys the Kiln with CREATE2 and reverts unless its address carries exactly the permission bits for beforeSwap, afterSwap, beforeSwapReturnDelta and afterSwapReturnDelta and no others; (2) initializes the ETH/ZTO pool on the PoolManager with lpFee, tickSpacing and the Kiln as hook at sqrtPriceX96; emits Opened(kiln, poolId). No liquidity is added by the Launcher: the deployer adds a ZTO-only range position later through the normal PositionManager, so the Kiln must not restrict liquidity in any way (no liquidity callbacks).
KILN, FEE PASS. On every swap in its pool the Kiln reads pepes = PEPEO.balanceOf(tx.origin) (routers are msg.sender; tx.origin is the trader) and picks the highest tier whose minPepes <= pepes. The pool's static lpFee goes to liquidity as usual; on top, the Kiln takes kilnCut of the swap as its cut, ALWAYS IN ZTO: when ZTO is the input, from the input (beforeSwap return delta on the specified currency for exact-input, afterSwap on the unspecified for exact-output); when ETH is the input, from the ZTO output (afterSwap return delta for exact-input, beforeSwap for exact-output). Work out each of the four cases so the trader is charged kilnCut of the ZTO side and the pool's accounting settles. The cut is taken from the PoolManager into the Kiln as real ZTO (poolManager.take) and added to reserve. Tier 21 pays no cut at all. Emit Passed(trader, pepes, kilnCut, ztoTaken) per swap. No block-held guard; README states that a pass only needs to be in the wallet during the swap.
KILN, PIECES. State: reserve (ZTO held for pieces, only grows by cuts, seeds and sales of pieces; only shrinks by buying pieces), inventory (ids held). Views: bid() = reserve / depth; ask() = bid() * (10000 + spreadBps) / 10000; inventory(), reserve(), tierOf(address), poolKey(). sell(uint256 id): the caller's PEPEO piece is pulled with transferFrom (caller approves first), price = bid() before the transfer, reserve -= price, ZTO.transfer(caller, price) requiring the bool, emits Sold(id, seller, price); reverts if bid() is 0. buy(uint256 id): id must be in inventory; price = ask(); ZTO.transferFrom(caller, kiln, price) requiring the bool, reserve += price, piece sent to caller with transferFrom (never safeTransferFrom, no receiver callbacks), emits Bought(id, buyer, price). seed(uint256 amount): anyone adds ZTO to reserve by transferFrom, emits Seeded(from, amount). No other way moves ZTO or pieces. Pieces arriving by plain transfer without sell() are not inventory and are stuck; README says so. Because bid is reserve/depth it is always payable, falls geometrically as pieces come in and rises with every cut, seed and sale.
TESTS against the real v4 PoolManager (vendor v4-core and v4-periphery test routers) with a mock ZTO and a mock ERC-721: open() once and only at an address with the right bits; a ZTO-only range position above the opening price added through the test liquidity router; swaps in all four cases (ETH in / ZTO in, exact in / exact out) for wallets holding 0, 1, 4 and 21 pieces, checking the ZTO cut equals kilnCut of the ZTO side within rounding, that tier 21 pays nothing, that the cut landed in reserve, and that the trader also paid lpFee; sell() pays bid and bid falls afterwards; buy() charges ask and the piece leaves inventory; buy of an id not held reverts; sell at zero reserve reverts; seed() grows bid; reserve never exceeds the Kiln's ZTO balance; nobody can withdraw. README with the rules, the tier table and the two caveats (tx.origin, stuck transfers). BUILD: solidity 0.8.26, optimizer + via-IR (via_ir = true, optimizer_runs = 1), custom errors only, no ReentrancyGuard (external token calls last), Kiln deployed code under 12,000 bytes. Slither: multiply before dividing; string.concat not encodePacked.
Work
- Posted15 minto the first attempt
Build contract projectAgent #1589569 files changed
Implemented Launcher and Kiln, vendored dependencies, Sepolia preparation tools, and documentation.
forge buildpassed.forge test: 43 passed.forge fmt --checkpassed.- Kiln runtime: 5,356 bytes.
The README documents deployment, fee accounting, caveats, and the ZTO-only range price convention.
No transactions were broadcast.
ran oncodex · gpt-6-astra · 6 turns · 13m 31s · 99.8K in · 33K out · 1.2M cachedsubmission60330fb6cf5277050b42b21dc4dcbbdcf68d2f575e2e75810b7d323fba0b3933devicea68191e8efe5b6e44315e453e973bd43b4a9713c00a859ef8fd6fca907434b47started from0243d7da4a4337ae8b16bcdf15bb4ead736fd68fbundle4306fabe288a75456c2c080e547aab9b5e6c45876b33703382148e7e25769f8b · 678 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 569 files.gitignoreREADME.mdfoundry.tomllib/dependencies.jsonlib/forge-std/LICENSE-APACHElib/forge-std/LICENSE-MITlib/forge-std/src/Base.sollib/forge-std/src/Script.sollib/forge-std/src/StdAssertions.sollib/forge-std/src/StdChains.sollib/forge-std/src/StdCheats.sollib/forge-std/src/StdError.sollib/forge-std/src/StdInvariant.sollib/forge-std/src/StdJson.sollib/forge-std/src/StdMath.sollib/forge-std/src/StdStorage.sollib/forge-std/src/StdStyle.sollib/forge-std/src/StdToml.sollib/forge-std/src/StdUtils.sollib/forge-std/src/Test.sollib/forge-std/src/Vm.sollib/forge-std/src/console.sollib/forge-std/src/console2.sollib/forge-std/src/interfaces/IERC1155.sollib/forge-std/src/interfaces/IERC165.sollib/forge-std/src/interfaces/IERC20.sollib/forge-std/src/interfaces/IERC4626.sollib/forge-std/src/interfaces/IERC721.sollib/forge-std/src/interfaces/IMulticall3.sollib/forge-std/src/safeconsole.sollib/openzeppelin-contracts/LICENSElib/openzeppelin-contracts/contracts/access/AccessControl.sollib/openzeppelin-contracts/contracts/access/IAccessControl.sollib/openzeppelin-contracts/contracts/access/Ownable.sollib/openzeppelin-contracts/contracts/access/Ownable2Step.sollib/openzeppelin-contracts/contracts/access/README.adoclib/openzeppelin-contracts/contracts/access/extensions/AccessControlDefaultAdminRules.sollib/openzeppelin-contracts/contracts/access/extensions/AccessControlEnumerable.sollib/openzeppelin-contracts/contracts/access/extensions/IAccessControlDefaultAdminRules.sollib/openzeppelin-contracts/contracts/access/extensions/IAccessControlEnumerable.sollib/openzeppelin-contracts/contracts/access/manager/AccessManaged.sollib/openzeppelin-contracts/contracts/access/manager/AccessManager.sollib/openzeppelin-contracts/contracts/access/manager/AuthorityUtils.sollib/openzeppelin-contracts/contracts/access/manager/IAccessManaged.sollib/openzeppelin-contracts/contracts/access/manager/IAccessManager.sollib/openzeppelin-contracts/contracts/access/manager/IAuthority.sollib/openzeppelin-contracts/contracts/finance/README.adoclib/openzeppelin-contracts/contracts/finance/VestingWallet.sollib/openzeppelin-contracts/contracts/governance/Governor.sollib/openzeppelin-contracts/contracts/governance/IGovernor.sollib/openzeppelin-contracts/contracts/governance/README.adoclib/openzeppelin-contracts/contracts/governance/TimelockController.sollib/openzeppelin-contracts/contracts/governance/extensions/GovernorCountingSimple.sollib/openzeppelin-contracts/contracts/governance/extensions/GovernorPreventLateQuorum.sollib/openzeppelin-contracts/contracts/governance/extensions/GovernorSettings.sollib/openzeppelin-contracts/contracts/governance/extensions/GovernorStorage.sollib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockAccess.sollib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockCompound.sollib/openzeppelin-contracts/contracts/governance/extensions/GovernorTimelockControl.sollib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotes.sollib/openzeppelin-contracts/contracts/governance/extensions/GovernorVotesQuorumFraction.sollib/openzeppelin-contracts/contracts/governance/utils/IVotes.sollib/openzeppelin-contracts/contracts/governance/utils/Votes.sollib/openzeppelin-contracts/contracts/interfaces/IERC1155.sollib/openzeppelin-contracts/contracts/interfaces/IERC1155MetadataURI.sollib/openzeppelin-contracts/contracts/interfaces/IERC1155Receiver.sollib/openzeppelin-contracts/contracts/interfaces/IERC1271.sollib/openzeppelin-contracts/contracts/interfaces/IERC1363.sollib/openzeppelin-contracts/contracts/interfaces/IERC1363Receiver.sollib/openzeppelin-contracts/contracts/interfaces/IERC1363Spender.sollib/openzeppelin-contracts/contracts/interfaces/IERC165.sollib/openzeppelin-contracts/contracts/interfaces/IERC1820Implementer.sollib/openzeppelin-contracts/contracts/interfaces/IERC1820Registry.sollib/openzeppelin-contracts/contracts/interfaces/IERC1967.sollib/openzeppelin-contracts/contracts/interfaces/IERC20.sollib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sollib/openzeppelin-contracts/contracts/interfaces/IERC2309.sollib/openzeppelin-contracts/contracts/interfaces/IERC2612.sollib/openzeppelin-contracts/contracts/interfaces/IERC2981.sollib/openzeppelin-contracts/contracts/interfaces/IERC3156.sollib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashBorrower.sollib/openzeppelin-contracts/contracts/interfaces/IERC3156FlashLender.sollib/openzeppelin-contracts/contracts/interfaces/IERC4626.sollib/openzeppelin-contracts/contracts/interfaces/IERC4906.sollib/openzeppelin-contracts/contracts/interfaces/IERC5267.sollib/openzeppelin-contracts/contracts/interfaces/IERC5313.sollib/openzeppelin-contracts/contracts/interfaces/IERC5805.sollib/openzeppelin-contracts/contracts/interfaces/IERC6372.sollib/openzeppelin-contracts/contracts/interfaces/IERC721.sollib/openzeppelin-contracts/contracts/interfaces/IERC721Enumerable.sollib/openzeppelin-contracts/contracts/interfaces/IERC721Metadata.sollib/openzeppelin-contracts/contracts/interfaces/IERC721Receiver.sollib/openzeppelin-contracts/contracts/interfaces/IERC777.sollib/openzeppelin-contracts/contracts/interfaces/IERC777Recipient.sollib/openzeppelin-contracts/contracts/interfaces/IERC777Sender.sollib/openzeppelin-contracts/contracts/interfaces/README.adoclib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sollib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sollib/openzeppelin-contracts/contracts/metatx/ERC2771Context.sollib/openzeppelin-contracts/contracts/metatx/ERC2771Forwarder.sollib/openzeppelin-contracts/contracts/metatx/README.adoclib/openzeppelin-contracts/contracts/mocks/AccessManagedTarget.sollib/openzeppelin-contracts/contracts/mocks/ArraysMock.sollib/openzeppelin-contracts/contracts/mocks/AuthorityMock.sollib/openzeppelin-contracts/contracts/mocks/Base64Dirty.sollib/openzeppelin-contracts/contracts/mocks/CallReceiverMock.sollib/openzeppelin-contracts/contracts/mocks/ContextMock.sollib/openzeppelin-contracts/contracts/mocks/DummyImplementation.sollib/openzeppelin-contracts/contracts/mocks/EIP712Verifier.sollib/openzeppelin-contracts/contracts/mocks/ERC1271WalletMock.sollib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165InterfacesSupported.sollib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MaliciousData.sollib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165MissingData.sollib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165NotSupported.sollib/openzeppelin-contracts/contracts/mocks/ERC165/ERC165ReturnBomb.sollib/openzeppelin-contracts/contracts/mocks/ERC2771ContextMock.sollib/openzeppelin-contracts/contracts/mocks/ERC3156FlashBorrowerMock.sollib/openzeppelin-contracts/contracts/mocks/EtherReceiverMock.sollib/openzeppelin-contracts/contracts/mocks/InitializableMock.sollib/openzeppelin-contracts/contracts/mocks/MulticallTest.sollib/openzeppelin-contracts/contracts/mocks/MultipleInheritanceInitializableMocks.sollib/openzeppelin-contracts/contracts/mocks/PausableMock.sollib/openzeppelin-contracts/contracts/mocks/ReentrancyAttack.sollib/openzeppelin-contracts/contracts/mocks/ReentrancyMock.sollib/openzeppelin-contracts/contracts/mocks/RegressionImplementation.sollib/openzeppelin-contracts/contracts/mocks/SingleInheritanceInitializableMocks.sollib/openzeppelin-contracts/contracts/mocks/Stateless.sollib/openzeppelin-contracts/contracts/mocks/StorageSlotMock.sollib/openzeppelin-contracts/contracts/mocks/TimelockReentrant.sollib/openzeppelin-contracts/contracts/mocks/UpgradeableBeaconMock.sollib/openzeppelin-contracts/contracts/mocks/VotesMock.sollib/openzeppelin-contracts/contracts/mocks/compound/CompTimelock.sollib/openzeppelin-contracts/contracts/mocks/docs/ERC20WithAutoMinerReward.sollib/openzeppelin-contracts/contracts/mocks/docs/ERC4626Fees.sollib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessControlERC20MintBase.sollib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessControlERC20MintMissing.sollib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessControlERC20MintOnlyRole.sollib/openzeppelin-contracts/contracts/mocks/docs/access-control/AccessManagedERC20MintBase.sollib/openzeppelin-contracts/contracts/mocks/docs/access-control/MyContractOwnable.sollib/openzeppelin-contracts/contracts/mocks/docs/governance/MyGovernor.sollib/openzeppelin-contracts/contracts/mocks/docs/governance/MyToken.sollib/openzeppelin-contracts/contracts/mocks/docs/governance/MyTokenTimestampBased.sollib/openzeppelin-contracts/contracts/mocks/docs/governance/MyTokenWrapped.sollib/openzeppelin-contracts/contracts/mocks/governance/GovernorMock.sollib/openzeppelin-contracts/contracts/mocks/governance/GovernorPreventLateQuorumMock.sollib/openzeppelin-contracts/contracts/mocks/governance/GovernorStorageMock.sollib/openzeppelin-contracts/contracts/mocks/governance/GovernorTimelockAccessMock.sollib/openzeppelin-contracts/contracts/mocks/governance/GovernorTimelockCompoundMock.sollib/openzeppelin-contracts/contracts/mocks/governance/GovernorTimelockControlMock.sollib/openzeppelin-contracts/contracts/mocks/governance/GovernorVoteMock.sollib/openzeppelin-contracts/contracts/mocks/governance/GovernorWithParamsMock.sollib/openzeppelin-contracts/contracts/mocks/proxy/BadBeacon.sollib/openzeppelin-contracts/contracts/mocks/proxy/ClashingImplementation.sollib/openzeppelin-contracts/contracts/mocks/proxy/UUPSUpgradeableMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC1155ReceiverMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20ApprovalMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20DecimalsMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20ExcessDecimalsMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20FlashMintMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20ForceApproveMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20Mock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20MulticallMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20NoReturnMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20Reentrant.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20ReturnFalseMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC20VotesLegacyMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC4626LimitsMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC4626Mock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC4626OffsetMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC4646FeesMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC721ConsecutiveEnumerableMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC721ConsecutiveMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC721ReceiverMock.sollib/openzeppelin-contracts/contracts/mocks/token/ERC721URIStorageMock.sollib/openzeppelin-contracts/contracts/mocks/token/VotesTimestamp.sollib/openzeppelin-contracts/contracts/package.jsonlib/openzeppelin-contracts/contracts/proxy/Clones.sollib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sollib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Utils.sollib/openzeppelin-contracts/contracts/proxy/Proxy.sollib/openzeppelin-contracts/contracts/proxy/README.adoclib/openzeppelin-contracts/contracts/proxy/beacon/BeaconProxy.sollib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sollib/openzeppelin-contracts/contracts/proxy/beacon/UpgradeableBeacon.sollib/openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sollib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sollib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sollib/openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sollib/openzeppelin-contracts/contracts/token/ERC1155/ERC1155.sollib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sollib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155Receiver.sollib/openzeppelin-contracts/contracts/token/ERC1155/README.adoclib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Burnable.sollib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Pausable.sollib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155Supply.sollib/openzeppelin-contracts/contracts/token/ERC1155/extensions/ERC1155URIStorage.sollib/openzeppelin-contracts/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sollib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Holder.sollib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sollib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sollib/openzeppelin-contracts/contracts/token/ERC20/README.adoclib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sollib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Capped.sollib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20FlashMint.sollib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Pausable.sollib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Permit.sollib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Votes.sollib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Wrapper.sollib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sollib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sollib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sollib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sollib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sollib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sollib/openzeppelin-contracts/contracts/token/ERC721/IERC721Receiver.sollib/openzeppelin-contracts/contracts/token/ERC721/README.adoclib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Burnable.sollib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Consecutive.sollib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Enumerable.sollib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Pausable.sollib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Royalty.sollib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sollib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Votes.sollib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721Wrapper.sollib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Enumerable.sollib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sollib/openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sollib/openzeppelin-contracts/contracts/token/common/ERC2981.sollib/openzeppelin-contracts/contracts/token/common/README.adoclib/openzeppelin-contracts/contracts/utils/Address.sollib/openzeppelin-contracts/contracts/utils/Arrays.sollib/openzeppelin-contracts/contracts/utils/Base64.sollib/openzeppelin-contracts/contracts/utils/Context.sollib/openzeppelin-contracts/contracts/utils/Create2.sollib/openzeppelin-contracts/contracts/utils/Multicall.sollib/openzeppelin-contracts/contracts/utils/Nonces.sollib/openzeppelin-contracts/contracts/utils/Pausable.sollib/openzeppelin-contracts/contracts/utils/README.adoclib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sollib/openzeppelin-contracts/contracts/utils/ShortStrings.sollib/openzeppelin-contracts/contracts/utils/StorageSlot.sollib/openzeppelin-contracts/contracts/utils/Strings.sollib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sollib/openzeppelin-contracts/contracts/utils/cryptography/EIP712.sollib/openzeppelin-contracts/contracts/utils/cryptography/MerkleProof.sollib/openzeppelin-contracts/contracts/utils/cryptography/MessageHashUtils.sollib/openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sollib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sollib/openzeppelin-contracts/contracts/utils/introspection/ERC165Checker.sollib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sollib/openzeppelin-contracts/contracts/utils/math/Math.sollib/openzeppelin-contracts/contracts/utils/math/SafeCast.sollib/openzeppelin-contracts/contracts/utils/math/SignedMath.sollib/openzeppelin-contracts/contracts/utils/structs/BitMaps.sollib/openzeppelin-contracts/contracts/utils/structs/Checkpoints.sollib/openzeppelin-contracts/contracts/utils/structs/DoubleEndedQueue.sollib/openzeppelin-contracts/contracts/utils/structs/EnumerableMap.sollib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sollib/openzeppelin-contracts/contracts/utils/types/Time.sollib/openzeppelin-contracts/contracts/vendor/compound/ICompoundTimelock.sollib/openzeppelin-contracts/contracts/vendor/compound/LICENSElib/permit2/LICENSElib/permit2/src/AllowanceTransfer.sollib/permit2/src/EIP712.sollib/permit2/src/Permit2.sollib/permit2/src/PermitErrors.sollib/permit2/src/SignatureTransfer.sollib/permit2/src/interfaces/IAllowanceTransfer.sollib/permit2/src/interfaces/IDAIPermit.sollib/permit2/src/interfaces/IEIP712.sollib/permit2/src/interfaces/IERC1271.sollib/permit2/src/interfaces/IPermit2.sollib/permit2/src/interfaces/ISignatureTransfer.sollib/permit2/src/libraries/Allowance.sollib/permit2/src/libraries/Permit2Lib.sollib/permit2/src/libraries/PermitHash.sollib/permit2/src/libraries/SafeCast160.sollib/permit2/src/libraries/SignatureVerification.sollib/solmate/LICENSElib/solmate/src/auth/Auth.sollib/solmate/src/auth/Owned.sollib/solmate/src/auth/authorities/MultiRolesAuthority.sollib/solmate/src/auth/authorities/RolesAuthority.sollib/solmate/src/mixins/ERC4626.sollib/solmate/src/test/Auth.t.sollib/solmate/src/test/Bytes32AddressLib.t.sollib/solmate/src/test/CREATE3.t.sollib/solmate/src/test/DSTestPlus.t.sollib/solmate/src/test/ERC1155.t.sollib/solmate/src/test/ERC20.t.sollib/solmate/src/test/ERC4626.t.sollib/solmate/src/test/ERC6909.t.sollib/solmate/src/test/ERC721.t.sollib/solmate/src/test/FixedPointMathLib.t.sollib/solmate/src/test/LibString.t.sollib/solmate/src/test/MerkleProofLib.t.sollib/solmate/src/test/MultiRolesAuthority.t.sollib/solmate/src/test/Owned.t.sollib/solmate/src/test/ReentrancyGuard.t.sollib/solmate/src/test/RolesAuthority.t.sollib/solmate/src/test/SSTORE2.t.sollib/solmate/src/test/SafeCastLib.t.sollib/solmate/src/test/SafeTransferLib.t.sollib/solmate/src/test/SignedWadMath.t.sollib/solmate/src/test/WETH.t.sollib/solmate/src/test/utils/DSInvariantTest.sollib/solmate/src/test/utils/DSTestPlus.sollib/solmate/src/test/utils/Hevm.sollib/solmate/src/test/utils/mocks/MockAuthChild.sollib/solmate/src/test/utils/mocks/MockAuthority.sollib/solmate/src/test/utils/mocks/MockERC1155.sollib/solmate/src/test/utils/mocks/MockERC20.sollib/solmate/src/test/utils/mocks/MockERC4626.sollib/solmate/src/test/utils/mocks/MockERC6909.sollib/solmate/src/test/utils/mocks/MockERC721.sollib/solmate/src/test/utils/mocks/MockOwned.sollib/solmate/src/test/utils/weird-tokens/MissingReturnToken.sollib/solmate/src/test/utils/weird-tokens/ReturnsFalseToken.sollib/solmate/src/test/utils/weird-tokens/ReturnsGarbageToken.sollib/solmate/src/test/utils/weird-tokens/ReturnsTooLittleToken.sollib/solmate/src/test/utils/weird-tokens/ReturnsTooMuchToken.sollib/solmate/src/test/utils/weird-tokens/ReturnsTwoToken.sollib/solmate/src/test/utils/weird-tokens/RevertingToken.sollib/solmate/src/tokens/ERC1155.sollib/solmate/src/tokens/ERC20.sollib/solmate/src/tokens/ERC6909.sollib/solmate/src/tokens/ERC721.sollib/solmate/src/tokens/WETH.sollib/solmate/src/utils/Bytes32AddressLib.sollib/solmate/src/utils/CREATE3.sollib/solmate/src/utils/FixedPointMathLib.sollib/solmate/src/utils/LibString.sollib/solmate/src/utils/MerkleProofLib.sollib/solmate/src/utils/ReentrancyGuard.sollib/solmate/src/utils/SSTORE2.sollib/solmate/src/utils/SafeCastLib.sollib/solmate/src/utils/SafeTransferLib.sollib/solmate/src/utils/SignedWadMath.sollib/v4-core/licenses/BUSL_LICENSElib/v4-core/licenses/MIT_LICENSElib/v4-core/src/ERC6909.sollib/v4-core/src/ERC6909Claims.sollib/v4-core/src/Extsload.sollib/v4-core/src/Exttload.sollib/v4-core/src/NoDelegateCall.sollib/v4-core/src/PoolManager.sollib/v4-core/src/ProtocolFees.sollib/v4-core/src/interfaces/IExtsload.sollib/v4-core/src/interfaces/IExttload.sollib/v4-core/src/interfaces/IHooks.sollib/v4-core/src/interfaces/IPoolManager.sollib/v4-core/src/interfaces/IProtocolFees.sollib/v4-core/src/interfaces/callback/IUnlockCallback.sollib/v4-core/src/interfaces/external/IERC20Minimal.sollib/v4-core/src/interfaces/external/IERC6909Claims.sollib/v4-core/src/libraries/BitMath.sollib/v4-core/src/libraries/CurrencyDelta.sollib/v4-core/src/libraries/CurrencyReserves.sollib/v4-core/src/libraries/CustomRevert.sollib/v4-core/src/libraries/FixedPoint128.sollib/v4-core/src/libraries/FixedPoint96.sollib/v4-core/src/libraries/FullMath.sollib/v4-core/src/libraries/Hooks.sollib/v4-core/src/libraries/LPFeeLibrary.sollib/v4-core/src/libraries/LiquidityMath.sollib/v4-core/src/libraries/Lock.sollib/v4-core/src/libraries/NonzeroDeltaCount.sollib/v4-core/src/libraries/ParseBytes.sollib/v4-core/src/libraries/Pool.sollib/v4-core/src/libraries/Position.sollib/v4-core/src/libraries/ProtocolFeeLibrary.sollib/v4-core/src/libraries/SafeCast.sollib/v4-core/src/libraries/SqrtPriceMath.sollib/v4-core/src/libraries/StateLibrary.sollib/v4-core/src/libraries/SwapMath.sollib/v4-core/src/libraries/TickBitmap.sollib/v4-core/src/libraries/TickMath.sollib/v4-core/src/libraries/TransientStateLibrary.sollib/v4-core/src/libraries/UnsafeMath.sollib/v4-core/src/test/ActionsRouter.sollib/v4-core/src/test/BaseTestHooks.sollib/v4-core/src/test/CurrencyTest.sollib/v4-core/src/test/CustomCurveHook.sollib/v4-core/src/test/DeltaReturningHook.sollib/v4-core/src/test/DynamicFeesTestHook.sollib/v4-core/src/test/DynamicReturnFeeTestHook.sollib/v4-core/src/test/EmptyRevertContract.sollib/v4-core/src/test/EmptyTestHooks.sollib/v4-core/src/test/FeeTakingHook.sollib/v4-core/src/test/Fuzzers.sollib/v4-core/src/test/HooksTest.sollib/v4-core/src/test/LPFeeTakingHook.sollib/v4-core/src/test/LiquidityMathTest.sollib/v4-core/src/test/MockContract.sollib/v4-core/src/test/MockERC6909Claims.sollib/v4-core/src/test/MockHooks.sollib/v4-core/src/test/NativeERC20.sollib/v4-core/src/test/NoDelegateCallTest.sollib/v4-core/src/test/PoolClaimsTest.sollib/v4-core/src/test/PoolDonateTest.sollib/v4-core/src/test/PoolEmptyUnlockTest.sollib/v4-core/src/test/PoolModifyLiquidityTest.sollib/v4-core/src/test/PoolModifyLiquidityTestNoChecks.sollib/v4-core/src/test/PoolNestedActionsTest.sollib/v4-core/src/test/PoolSwapTest.sollib/v4-core/src/test/PoolTakeTest.sollib/v4-core/src/test/PoolTestBase.sollib/v4-core/src/test/ProtocolFeesImplementation.sollib/v4-core/src/test/ProxyPoolManager.sollib/v4-core/src/test/SkipCallsTestHook.sollib/v4-core/src/test/SqrtPriceMathEchidnaTest.sollib/v4-core/src/test/SwapRouterNoChecks.sollib/v4-core/src/test/TestERC20.sollib/v4-core/src/test/TestInvalidERC20.sollib/v4-core/src/test/TickMathEchidnaTest.sollib/v4-core/src/test/TickMathTest.sollib/v4-core/src/test/TickOverflowSafetyEchidnaTest.sollib/v4-core/src/types/BalanceDelta.sollib/v4-core/src/types/BeforeSwapDelta.sollib/v4-core/src/types/Currency.sollib/v4-core/src/types/PoolId.sollib/v4-core/src/types/PoolKey.sollib/v4-core/src/types/Slot0.sollib/v4-core/test/utils/AmountHelpers.sollib/v4-core/test/utils/Constants.sollib/v4-core/test/utils/CurrencySettler.sollib/v4-core/test/utils/Deployers.sollib/v4-core/test/utils/JavascriptFfi.sollib/v4-core/test/utils/LiquidityAmounts.sollib/v4-core/test/utils/Logger.sollib/v4-core/test/utils/NestedActions.t.sollib/v4-core/test/utils/SortTokens.sollib/v4-core/test/utils/SwapHelper.t.sollib/v4-core/test/utils/V3Helper.sollib/v4-periphery/LICENSElib/v4-periphery/src/PositionDescriptor.sollib/v4-periphery/src/PositionManager.sollib/v4-periphery/src/UniswapV4DeployerCompetition.sollib/v4-periphery/src/V4Router.sollib/v4-periphery/src/base/BaseActionsRouter.sollib/v4-periphery/src/base/BaseV4Quoter.sollib/v4-periphery/src/base/DeltaResolver.sollib/v4-periphery/src/base/EIP712_v4.sollib/v4-periphery/src/base/ERC721Permit_v4.sollib/v4-periphery/src/base/ImmutableState.sollib/v4-periphery/src/base/Multicall_v4.sollib/v4-periphery/src/base/NativeWrapper.sollib/v4-periphery/src/base/Notifier.sollib/v4-periphery/src/base/Permit2Forwarder.sollib/v4-periphery/src/base/PoolInitializer_v4.sollib/v4-periphery/src/base/ReentrancyLock.sollib/v4-periphery/src/base/SafeCallback.sollib/v4-periphery/src/base/UnorderedNonce.sollib/v4-periphery/src/base/hooks/BaseHook.sollib/v4-periphery/src/interfaces/IEIP712_v4.sollib/v4-periphery/src/interfaces/IERC721Permit_v4.sollib/v4-periphery/src/interfaces/IImmutableState.sollib/v4-periphery/src/interfaces/IMulticall_v4.sollib/v4-periphery/src/interfaces/INotifier.sollib/v4-periphery/src/interfaces/IPermit2Forwarder.sollib/v4-periphery/src/interfaces/IPoolInitializer_v4.sollib/v4-periphery/src/interfaces/IPositionDescriptor.sollib/v4-periphery/src/interfaces/IPositionManager.sollib/v4-periphery/src/interfaces/IStateView.sollib/v4-periphery/src/interfaces/ISubscriber.sollib/v4-periphery/src/interfaces/IUniswapV4DeployerCompetition.sollib/v4-periphery/src/interfaces/IUnorderedNonce.sollib/v4-periphery/src/interfaces/IV4Quoter.sollib/v4-periphery/src/interfaces/IV4Router.sollib/v4-periphery/src/interfaces/external/IWETH9.sollib/v4-periphery/src/lens/StateView.sollib/v4-periphery/src/lens/V4Quoter.sollib/v4-periphery/src/libraries/ActionConstants.sollib/v4-periphery/src/libraries/Actions.sollib/v4-periphery/src/libraries/AddressStringUtil.sollib/v4-periphery/src/libraries/BipsLibrary.sollib/v4-periphery/src/libraries/CalldataDecoder.sollib/v4-periphery/src/libraries/CurrencyRatioSortOrder.sollib/v4-periphery/src/libraries/Descriptor.sollib/v4-periphery/src/libraries/ERC721PermitHash.sollib/v4-periphery/src/libraries/HexStrings.sollib/v4-periphery/src/libraries/LiquidityAmounts.sollib/v4-periphery/src/libraries/Locker.sollib/v4-periphery/src/libraries/PathKey.sollib/v4-periphery/src/libraries/PositionConfig.sollib/v4-periphery/src/libraries/PositionConfigId.sollib/v4-periphery/src/libraries/PositionInfoLibrary.sollib/v4-periphery/src/libraries/QuoterRevert.sollib/v4-periphery/src/libraries/SVG.sollib/v4-periphery/src/libraries/SafeCurrencyMetadata.sollib/v4-periphery/src/libraries/SlippageCheck.sollib/v4-periphery/src/libraries/VanityAddressLib.sollib/v4-periphery/test/BaseActionsRouter.t.sollib/v4-periphery/test/DeltaResolver.t.sollib/v4-periphery/test/EIP712.t.sollib/v4-periphery/test/Multicall.t.sollib/v4-periphery/test/PositionDescriptor.t.sollib/v4-periphery/test/SafeCallback.t.sollib/v4-periphery/test/StateViewTest.t.sollib/v4-periphery/test/UniswapV4DeployerCompetition.t.sollib/v4-periphery/test/UnorderedNonce.t.sollib/v4-periphery/test/V4Quoter.t.sollib/v4-periphery/test/base64.sollib/v4-periphery/test/erc721Permit/ERC721Permit.permit.t.sollib/v4-periphery/test/erc721Permit/ERC721Permit.permitForAll.t.sollib/v4-periphery/test/libraries/BipsLibrary.t.sollib/v4-periphery/test/libraries/CalldataDecoder.t.sollib/v4-periphery/test/libraries/Descriptor.t.sollib/v4-periphery/test/libraries/PositionInfoLibrary.t.sollib/v4-periphery/test/libraries/SVG.t.sollib/v4-periphery/test/libraries/SafeCurrencyMetadata.t.sollib/v4-periphery/test/libraries/VanityAddressLib.t.sollib/v4-periphery/test/mocks/MockBadSubscribers.sollib/v4-periphery/test/mocks/MockBaseActionsRouter.sollib/v4-periphery/test/mocks/MockCalldataDecoder.sollib/v4-periphery/test/mocks/MockDeltaResolver.sollib/v4-periphery/test/mocks/MockERC721Permit.sollib/v4-periphery/test/mocks/MockFeeOnTransfer.sollib/v4-periphery/test/mocks/MockMulticall.sollib/v4-periphery/test/mocks/MockReenterHook.sollib/v4-periphery/test/mocks/MockSafeCallback.sollib/v4-periphery/test/mocks/MockSubscriber.sollib/v4-periphery/test/mocks/MockUnorderedNonce.sollib/v4-periphery/test/mocks/MockV4Router.sollib/v4-periphery/test/mocks/ReentrantToken.sollib/v4-periphery/test/position-managers/Execute.t.sollib/v4-periphery/test/position-managers/FeeCollection.t.sollib/v4-periphery/test/position-managers/IncreaseLiquidity.t.sollib/v4-periphery/test/position-managers/NativeToken.t.sollib/v4-periphery/test/position-managers/Permit.t.sollib/v4-periphery/test/position-managers/Permit2Forwarder.t.sollib/v4-periphery/test/position-managers/PositionManager.gas.t.sollib/v4-periphery/test/position-managers/PositionManager.modifyLiquidities.t.sollib/v4-periphery/test/position-managers/PositionManager.multicall.t.sollib/v4-periphery/test/position-managers/PositionManager.notifier.t.sollib/v4-periphery/test/position-managers/PositionManager.t.sollib/v4-periphery/test/router/Payments.gas.t.sollib/v4-periphery/test/router/Payments.t.sollib/v4-periphery/test/router/V4Router.gas.t.sollib/v4-periphery/test/router/V4Router.t.sollib/v4-periphery/test/script/DeployPoolManager.t.sollib/v4-periphery/test/script/DeployPoolMofifyLiquidityTest.t.sollib/v4-periphery/test/script/DeployPoolSwapTest.t.sollib/v4-periphery/test/shared/Deploy.sollib/v4-periphery/test/shared/FeeMath.sollib/v4-periphery/test/shared/HookModifyLiquidities.sollib/v4-periphery/test/shared/HookSavesDelta.sollib/v4-periphery/test/shared/LiquidityOperations.sollib/v4-periphery/test/shared/Permit2SignatureHelpers.sollib/v4-periphery/test/shared/Planner.sollib/v4-periphery/test/shared/PositionConfig.sollib/v4-periphery/test/shared/PosmTestSetup.sollib/v4-periphery/test/shared/RoutingTestHelpers.sollib/v4-periphery/test/shared/fuzz/LiquidityFuzzers.solremappings.txtscript/MineSalt.s.solscript/SepoliaParameters.s.solsrc/Kiln.solsrc/KilnConfig.solsrc/Launcher.solsrc/Sepolia.soltest/KilnBase.soltest/Launcher.t.soltest/Pieces.t.soltest/ReserveInvariant.t.soltest/Swaps.t.soltest/Tools.t.soltest/mocks/MockTokens.soltools/SaltMiner.solManifestAgent #55needs input
Failed in the task.
needs input: The compiled Launcher and Kiln constructors require uint256[] thresholds and uint24[] cuts, but launch.json permits only static ABI words. They also take int24 spacing, outside the documented supported constructor types. No manifest can both satisfy the deployment format and match the accepted constructors. Resolving this requires a revised accepted constructor ABI or deployment-format support, neither of which can be changed in this manifest-only assignment. — Should the accepted constructors be revised to use supported static arguments while preserving the approved tiers, or should the deployment format be extended to support their existing int24 and dynamic-array arguments?
ran oncodex · gpt-6-astra · 5 turns · 3m 5s · 24.2K in · 3.2K out · 332K cachedsubmissiond6c72ba390ad85b8354b53e7600601d96a68e0c3700d1766c1819a46cb8bdd0ddevicebc30da9213bc5d7a1b31c769e6b34715cf40a5bc6ca18e1125e12411e6a108bcstarted from873c26dea52c6c1b5b8cc07291662fbc7982dfa7bundlenoneapplied on4306fabe288a75456c2c080e547aab9b5e6c45876b33703382148e7e25769f8bmay writelaunch.jsonAudit permissionsAgent #976found 2 low, 1 info
The review is complete and the findings file is written and validated against the tree.
Outcome. No critical or high defects in the permission model. The Kiln and Launcher have no owner, no withdrawal path, correct caller and pool-key guards on both swap callbacks, and the sell/buy bonding curve cannot be drained by any ordering of sells and buys. I report two low findings and one informational trust note in
.imd-findings.json, each with a concrete reproduction executed against the vendored v4 PoolManager in a scratch test.Findings written:
- Low,
Launcher.open: the pool can be pre-initialized by anyone at the CREATE2-predicted Kiln address because initialize needs no code there and the Kiln has no initialize hook bits. A mempool watcher can block every salt the deployer broadcasts, repeatably and cheaply, or front-runopenwith the deployer's salt and their own price. No funds at risk and the price resets with a 1-wei swap, but the README's "mine a new salt" remedy does not terminate. - Low,
Kiln.afterSwap: the ZTO-input cut is taken as real tokens before the trader settles, so when the manager holds less ZTO than the cut, only pass-less traders' swaps revert while 21-piece holders trade normally. Tier-dependent liveness, masked on a shared manager by other pools' WETH. - Info,
Sepolia.solPEPEO address: the on-chain stand-in is an unverified "Ochre" contract with an admin and its own sale logic. Transfers simulate fine and I found no path that moves a Kiln-held piece, so this is recorded as a trust assumption, not a defect.
Coverage. All five listed entry points have rows, plus rows for beforeSwap, the reserve-backing and round-trip invariants, the tx.origin design choice, and the constructors. Everything in the Access Control, Asymmetry and Trust Gap guides was checked. The one thing I could not reach is the Sepolia piece contract's source, which is unverified on Sourcify and Blockscout.
Files touched. Only
.imd-findings.jsonand the scratch testtest/scratch/Leads.t.sol. No source, test or configuration file was changed. No proof files were attached since no finding is high or critical.ran onclaude · claude-fable-5-1 · 43 turns · 14m 22s · 578 in · 48.5K out · 2.1M cachedsubmission12d564719929569349477e5147be42adc17760f1de004bb3ea8864ea1de2dc72devicea7b8747ac077deb9a656205f7afe5268f40a5ea9affa1e70d5157a71d70d3742started from873c26dea52c6c1b5b8cc07291662fbc7982dfa7bundlenoneapplied on4306fabe288a75456c2c080e547aab9b5e6c45876b33703382148e7e25769f8bLauncher.open can be blocked indefinitely or price-hijacked by anyone watching the mempoolsrc/Launcher.sol:56
ZTO-input swaps by pass-less traders revert whenever the PoolManager holds less ZTO than the cut, while 21-piece holders' swaps succeedsrc/Kiln.sol:155
Rehearsal PEPEO dependency is an unverified contract with an admin and its own sale logic; Kiln inventory assumes custody can only leave via buy()src/Sepolia.sol:8
- Low,
Write foundry testsAgent #1172refused
Refused by Codex's safety filter
This content was flagged for possible cybersecurity risk. If this seems wrong, try rephrasing your request. If you’re doing authorized security work that requires more cyber permissive safeguards, apply for Daybreak access via https://platform.openai.com/settings/organization/status-and-access before retrying.
ran oncodex · gpt-6-astra · 5 turns · 14m 42ssubmission09eef3c1c1eb5b09182fa0d9a3ce98f9ba08d87b4d36f9ddd902627917e8d656deviceb94d8a9fa9123fb17749c3be6d28e190273f1cd7570a5fb43a995daa4da92e07started from873c26dea52c6c1b5b8cc07291662fbc7982dfa7bundlenoneapplied on4306fabe288a75456c2c080e547aab9b5e6c45876b33703382148e7e25769f8bmay writetesttest/**Audit flowAgent #123found 2 medium, 3 low
The review is complete and
.imd-findings.jsonholds five findings plus a nine-row coverage record. No source files were changed. The only additions are the findings file and two scratch tests undertest/scratch/, which are ignored by git.What I checked. I traced all four swap cases through v4's
Hooks.beforeSwapandHooks.afterSwapdelta plumbing. The specified and unspecified sides, sign conventions, gross-up math, thePartialFillcheck and the finalswapDelta - hookDeltaall land the cut on currency1 and settle the pool's accounting. The 43 shipped tests pass. The piece functions follow checks-effects-interactions, the inventory swap-and-pop handles the last-element case, and the reserve-backing invariant holds on every path. The 14-bit address check in the Launcher parses as intended because&binds tighter than!=in Solidity. I also probed the live Sepolia dependencies: the PEPEO address is an OpenZeppelin v5 ERC-721 named "Ochre" with 7 minted tokens, and WETH and the PoolManager are present.Findings, most severe first:
- Medium,
src/Kiln.sol:155.afterSwappulls real ZTO withpoolManager.takewhile the trader's ZTO input is still unsettled. If the manager holds less ZTO than the cut, the transfer reverts. Once a ZTO-only launch range has been bought out, the ZTO-in direction is dead for every wallet with fewer than 21 passes. A proof test undertest/scratch/TakeLiveness.t.solfails on the current code and is attached to the finding. Crediting the cut as an ERC-6909 claim and burning lazily would fix it without changing the economics. - Medium,
src/Launcher.sol:27. The constructor takes two dynamic arrays and anint24, which the launch manifest's static-word encoder cannot express. The README concedes this. The brief asked for arrays, so this is a brief-versus-policy conflict that needs a scope decision. - Low,
src/Kiln.sol:113.tierOf(tx.origin)reverts when the origin is address zero. The real Sepolia PEPEO rejectsbalanceOf(0), so anyeth_callquote or simulation without afromaddress fails for this pool. - Low,
src/Kiln.sol:144. ZTO-specified swaps with a nonzero cut revert on any partial fill rather than filling partially. Documented, but it excludes price-bounded routers. - Low,
src/Launcher.sol:56. Anyone can pre-initialize the predicted pool to block a salt, and the permissionless opening price can be set at the minimum tick where no ZTO-only range exists. Both are recoverable and partly documented.
Coverage. All five listed entry points have rows.
buy,seedandsellhold.afterSwap,beforeSwapandopencarry findings. Three invariant rows record that the reserve stays backed, the hook never charges ETH, and no withdrawal or admin path exists.ran onclaude · claude-fable-5-1 · 40 turns · 15m 25s · 450 in · 48.7K out · 1.6M cachedsubmissiona7d4f39fe815f81077265a8dadb994adfce8deb80d668aa81e69b5b613998088devicefcb71e606c933181525a83d27f11eab9e58887a1363db3df621a159ae661b967started from873c26dea52c6c1b5b8cc07291662fbc7982dfa7bundlenoneapplied on4306fabe288a75456c2c080e547aab9b5e6c45876b33703382148e7e25769f8bmediumafterSwap takes real ZTO before the trader settles: every ZTO-in swap with a nonzero cut reverts whenever the PoolManager holds less ZTO than the cutsrc/Kiln.sol:155
proof · a Foundry test the fix has to passmediumLauncher constructor takes dynamic arrays and an int24, which the evm_contracts launch manifest encoder cannot represent, so the launch cannot be deployed through the project factory as writtensrc/Launcher.sol:27
tierOf(tx.origin) reverts for tx.origin == address(0), so every eth_call swap simulation (quoter, wallet gas estimation) without a from address fails against the real Sepolia PEPEOsrc/Kiln.sol:113
beforeSwap and afterSwap call PEPEO.balanceOf(tx.origin) unguarded.
The Sepolia rehearsal PEPEO at 0x0ce3157eac34eccdcff239738983976fabdefb2a is an OpenZeppelin v5 ERC721: cast call balanceOf(0x0) reverts with ERC721InvalidOwner(0x0000...). eth_call defaults from (and therefore tx.origin) to address(0) when integrators do not set it, which is the common path for V4Quoter and generic routing simulators, so every quote and every unsigned simulation of this pool reverts with a hook error even though the on-chain swap would succeed.
The mock in the repo tests has the same behaviour but no test exercises origin 0.
Minimal fix: in tierOf, treat address(0) as 0 passes (or wrap balanceOf in try/catch returning 0); the tier for a real sender is unchanged.
vm.prank(trader, address(0)); swapRouter.swap(key, SwapParams(true, -1e18, MIN_SQRT_PRICE+1), ...) with liquidity [-600,600].
Expected (as a quote): a filled ETH->ZTO swap at the 0-pass tier.
Actual: revert HookCallFailed wrapping ERC721InvalidOwner(0).
Confirmed live on Sepolia: cast call 0x0ce3157e...
'balanceOf(address)' 0x0 -> execution reverted ERC721InvalidOwner(0x0).
Scratch test test/scratch/Leads.t.sol test_lead_zeroOriginSwapReverts.
ZTO-specified swaps with a nonzero cut revert on any partial fill (price limit or exhausted liquidity) instead of filling partially as every other v4 pool doessrc/Kiln.sol:144
Liquidity [-600,600] 1e24.
Trader with 0 passes: swapRouter.swap(key, SwapParams(false, -1000e18, getSqrtPriceAtTick(1))). v4 without the hook returns a partial fill up to tick 1.
Actual: revert PartialFill (the repo's own test_partialZTOInputRevertsAtomically shows it).
Same trader with 21 passes: test_zeroTierAllowsPartialSpecifiedSwap succeeds with amount1 < 1000e18.
open() can be griefed by pre-initializing the predicted pool, and its permissionless price lets a front-runner open at a price where no ZTO-only range can be createdsrc/Launcher.sol:56
- Medium,
Audit economicsAgent #83found 1 medium, 2 low, 2 info
Review complete. The findings file is at
.imd-findings.jsonwith 5 findings and 11 coverage rows, and no tracked file was changed (the scratch tests and the findings file are excluded by the harness's git exclude list).What I found
- Medium, Kiln.afterSwap, line 155. The hook takes its ZTO cut out of the PoolManager as real tokens inside the swap, before the seller settles the ZTO they are paying in. When the manager holds less ZTO than the cut, every ZTO-in swap by a tier 0, 1 or 4 wallet reverts, even though the pool has the ETH to pay. On mainnet this state arrives as soon as buyers sweep the ZTO-only range, and an attacker can create it on purpose. A 21-pass wallet trades the same route fine. The Sepolia rehearsal masks it because the shared manager holds about 709 WETH. The proof in
test/scratch/TakeBeforeSettle.t.solfails on the current code and passed under a throwaway fix that falls back to ERC-6909 claims, which I reverted. - Low, Launcher.open, line 56. The pool key has no initialize hook bits, so anyone who sees a pending open can initialize the predicted pool first for about 50k gas and make open revert, repeatedly per salt. Opening is also first come, so a third party can choose the opening price. The README acknowledges both.
- Low, Kiln.tierOf, line 53. With tx.origin equal to the zero address, which is what eth_call quoting uses when no from is set, the OpenZeppelin-style PEPEO reverts on balanceOf. Every such quote fails. I confirmed the real Sepolia PEPEO reverts the same way.
- Info, README line 49. The supplied Sepolia PEPEO address is an ERC-721 named "Ochre" with a total supply of 7, so tiers 4 and 21 cannot be rehearsed as documented.
- Info, Launcher constructor, line 27. The two dynamic array arguments cannot be encoded by the static-word launch manifest, a conflict between the brief and the launch recipe that needs a scope decision.
What held. The four cut cases, the gross-up rounding, the PartialFill guard, the pool key check, the bid and ask snapshots, the swap-and-pop inventory, reserve backing, and the sell-then-buy round trip all behave as intended. Selling pieces and buying them back always costs the caller, and no sandwich of sell or buy is profitable. The existing 43 tests pass.
Coverage. All five listed entry points have rows: afterSwap, beforeSwap and open carry findings, buy, seed and sell hold, plus five invariant rows that hold. The Slither and Aderyn lines were checked and none turned into a finding.
ran onclaude · claude-fable-5-1 · 39 turns · 16m 24s · 418 in · 56.9K out · 1.6M cachedsubmission8bd6d46935a7d7804b8066e86f2e6dbc81ea9e274f8b7a671f819128680a5556deviced4b53d656a3fbe077c1d83443f002e7695b697ca46c860d0b0506438bf65a63bstarted from873c26dea52c6c1b5b8cc07291662fbc7982dfa7bundlenoneapplied on4306fabe288a75456c2c080e547aab9b5e6c45876b33703382148e7e25769f8bmediumafterSwap takes the ZTO cut as real tokens before the seller settles, so ZTO-in swaps with a cut revert whenever the PoolManager holds less ZTO than the cutsrc/Kiln.sol:155
proof · a Foundry test the fix has to passLauncher.open can be blocked repeatedly by pre-initializing the predicted pool key, and whoever lands open first sets the opening pricesrc/Launcher.sol:56
Deploy Launcher L; salt s with predicted Kiln P = SaltMiner.find(L, L.initCodeHash()).
Griefer calls manager.initialize(PoolKey(ETH, ZTO, 2000, 60, IHooks(P)), 1000*2^96) in the same block before L.open(s, 2^96).
Expected (deployer's view): open deploys P and initializes at 2^96.
Actual: open reverts (PoolAlreadyInitialized), L.kiln() stays address(0); repeat for every new salt.
Verified in test/scratch/Leads.t.sol test_preInitializingPredictedPoolBlocksOpen (passes = griefing works).
Swap callbacks read PEPEO.balanceOf(tx.origin); with tx.origin = address(0) (eth_call quotes without a from) the OpenZeppelin-style PEPEO reverts, so every quote simulation revertssrc/Kiln.sol:53
The supplied Sepolia PEPEO address is an ERC-721 named 'Ochre' with totalSupply 7, so tiers 4 and 21 cannot be rehearsed as documentedREADME.md:49
Economic Security (break dependencies), environment rather than code.
On-chain reads on 2026-10-08 (publicnode Sepolia RPC): the contract at 0x0ce3157eac34eccdcff239738983976fabdefb2a has code (16.5 KB), supportsInterface(0x80ac58cd) = true, name() = 'Ochre', symbol() = 'OCHRE', totalSupply() = 7, ownerOf(0) and ownerOf(1) are two EOAs, ownerOf(737) reverts ERC721NonexistentToken. balanceOf works, so swaps will not brick, but with 7 tokens in existence no wallet can hold 21 and reach the free tier, and only one wallet can hold 4.
The README's '737 IDs' and the four-tier rehearsal cannot be exercised on Sepolia unless the Ochre contract has an open mint. The ZTO (WETH, 18 decimals) and PoolManager addresses check out.
cast call 0x0ce3157eac34eccdcff239738983976fabdefb2a 'totalSupply()(uint256)' --rpc-url returns 7; 'name()(string)' returns "Ochre".
Expected per README: a 737-piece Pepeolithic collection.
A tier-21 swap therefore has no possible tx.origin on the rehearsal chain.
Launcher's constructor takes two dynamic arrays, which the evm_contracts launch manifest (static words only) cannot encodesrc/Launcher.sol:27
Policy conflict, outside the economic area but blocking for deployment through the service. The brief asks for tiers 'as two arrays', and the evm-contracts-launch recipe allows only address, uintN, bool and bytes32 constructor words.
A launch.json entry {"contract":"Launcher","constructorArgs":[zto, pepeo, poolManager, "60", "2000", ?, ?, "1500", "50"]} has no representation for positions 5 and 6, so the attested creation bytes cannot be produced by the manifest encoder; the README (line 76-78) already states this.
Resolution needs a scope decision: either the service encodes dynamic arrays, or the tiers are passed as four packed uint256 words / bytes32 (e.g. thresholds and cuts packed into fixed-size arguments) and expanded in the constructor.
Write launch.json for contract Launcher with the nine arguments above; the LaunchManifest schema only accepts strings of at most 96 characters per argument and the recipe's encoder only static words, so the two array arguments (ABI head+tail encoding) cannot be expressed; the protected deployment probe can only run with IMD_PROJECT_CODE_0 produced by some other encoder.
- Medium, Kiln.afterSwap, line 155. The hook takes its ZTO cut out of the PoolManager as real tokens inside the swap, before the seller settles the ZTO they are paying in. When the manager holds less ZTO than the cut, every ZTO-in swap by a tier 0, 1 or 4 wallet reverts, even though the pool has the ETH to pay. On mainnet this state arrives as soon as buyers sweep the ZTO-only range, and an attacker can create it on purpose. A 21-pass wallet trades the same route fine. The Sepolia rehearsal masks it because the shared manager holds about 709 WETH. The proof in
Audit mathAgent #754found 1 medium, 3 low, 1 info
Review complete. The findings file is at
.imd-findings.jsonwith 5 findings and 11 coverage rows; no source files were changed, and the scratch tests live only undertest/scratch/.Outcome. The swap math itself holds: I traced all four swap cases through the v4 core and Hooks library, confirmed the gross-up identity, the int128 bound checks and the hook-delta mapping, and fuzzed amounts from 1 wei to 1e6 wei on top of the existing suite with exact equality on the cut. The bid/ask and reserve accounting also hold. The defects found are at boundaries, not in the formulas.
Findings, by severity:
- Medium, with proof test. In the two ZTO-input swap cases the hook calls
poolManager.takein afterSwap before the trader has settled any ZTO. Once the launch's ZTO-only range is bought out, the manager holds only dust ZTO and every sell of ZTO by a wallet with a nonzero cut reverts inside take, while 21-pass wallets can still sell. Reproduced with the brief's launch flow against the real PoolManager; the attached proof test fails on the current code. - Low. The hook reads
PEPEO.balanceOf(tx.origin), and ERC-721 mandates a revert for the zero address, so any eth_call simulation or quote withoutfromreverts. - Low. Anyone can initialize the pool at the predicted Kiln address before open(), which then reverts with PoolAlreadyInitialized and burns that salt. The salt is visible in pending calldata, so this is repeatable.
- Low. buy() and sell() bind the caller to the execution-time ask or bid with no bound; a seed, cut or sale landing first changes the price paid.
- Info. For reserves under 2500 wei the bid does not fall after a sale, contrary to the brief's wording. Dust only.
Coverage. All five listed entry points have rows, plus beforeSwap and five invariant rows. The existing suite passes (43 tests). Slither's leads were checked and none is a defect.
ran onclaude · claude-fable-5-1 · 39 turns · 16m 29s · 418 in · 55.7K out · 1.6M cachedsubmissionf4b858bbf8bdcfac9387d6af7eaeeabea8d2b1674c275be3b0b843ecf899c6b5device3beb94a990617d4651d263ad3777d5af0836bb94b331f675dbe15bb8ae48e83dstarted from873c26dea52c6c1b5b8cc07291662fbc7982dfa7bundlenoneapplied on4306fabe288a75456c2c080e547aab9b5e6c45876b33703382148e7e25769f8bmediumafterSwap take() needs the PoolManager to already hold the cut in ZTO: every ZTO-in swap by a fee-paying wallet reverts once the pool's ZTO is bought outsrc/Kiln.sol:155
proof · a Foundry test the fix has to passSwap simulations and quotes with tx.origin = address(0) revert because the hook calls PEPEO.balanceOf(address(0))src/Kiln.sol:53
Boundary: external call PEPEO.balanceOf(tx.origin) in tierOf, reached from beforeSwap and afterSwap.
Assumption: balanceOf returns a number for any origin.
Actual: ERC-721 requires balanceOf(address(0)) to throw (OpenZeppelin reverts ERC721InvalidOwner(0)), and tx.origin is address(0) in every eth_call/estimateGas that omits
from- the way v4 Quoter and many integrations simulate swaps. Every such simulation of this pool reverts inside the hook, so quoting tools that do not setfromcannot price the pool, and the revert reason is the NFT error, not a Kiln error.Harmless on-chain (no real transaction has origin 0) but it breaks the off-chain quote path for integrators.
Fix: in tierOf, return the tier-0 cut when trader == address(0), or wrap balanceOf in a try/catch defaulting to the highest cut.
State: pool open with liquidity in [-600,600].
Input: vm.prank(address(0), address(0)); PoolSwapTest.swap(key, SwapParams(true, -0.1e18, MIN_SQRT_PRICE+1), ...).
Expected: a quote/delta like for any other origin.
Actual: revert (ERC721InvalidOwner(0) wrapped in HookCallFailed).
Also kiln.tierOf(address(0)) reverts with ERC721InvalidOwner(0x0) instead of returning (0, 13000).
Reproduced in test/scratch/OriginZeroAndOpenGrief.t.sol.
Anyone can initialize the pool at the predicted Kiln address first, making open() revert for that salt (repeatable front-run)src/Launcher.sol:56
State: fresh Launcher with the brief's args; salt mined so predicted & 0x3fff == 0x00cc.
Input: griefer calls poolManager.initialize(PoolKey(Currency(0), Currency(ZTO), 2000, 60, IHooks(predicted)), 2^96) - succeeds.
Then deployer calls launcher.open(salt, 2^96).
Expected per brief: open succeeds once.
Actual: revert PoolAlreadyInitialized, launcher.kiln() == 0, predicted.code.length == 0.
Reproduced in test/scratch/OriginZeroAndOpenGrief.t.sol::test_frontRunInitializeBlocksOpenForThatSalt.
buy()/sell() bind the caller to whatever ask()/bid() is at execution time; a cut, seed or sale landing first changes the price with no boundsrc/Kiln.sol:94
Numerical gap (boundary x invariant): the quoted price is read from reserve at execution, and reserve moves on every swap cut, seed, sell and buy by anyone. buy(id) pulls ask() with transferFrom against whatever allowance the buyer granted; a buyer who approved max (common wallet default) pays a higher ask than quoted if reserve grows in between; a seller receives a lower bid if another sale lands first.
No caller-supplied bound exists and the brief fixes the signatures as buy(uint256)/sell(uint256). README documents this and tells callers to wrap. Reported as low so the author can decide; a minimal, signature-preserving mitigation is not possible, a maxPrice/minPrice overload would be.
State: reserve = 5000e18, so bid = 100e18, ask = 115e18.
Buyer approves type(uint256).max and submits buy(7) expecting 115e18.
Before it lands, any account calls seed(5000e18) (or a whale swap deposits a cut): reserve = 10000e18, ask = 230e18. buy(7) executes and transfers 230e18 from the buyer.
Expected by the buyer: 115e18.
Mirror: seller expecting 100e18 receives 98e18 if another sell(id) executes first (reserve 4900e18 -> bid 98e18).
bid() does not fall after a sale when reserve mod 50 >= bid (only at dust reserves)src/Kiln.sol:66
Precision x invariant at the boundary: the brief states 'sell() pays bid and bid falls afterwards'. With reserve R = 50k + j (0 <= j < 50) a sale pays k and leaves 49k + j, whose bid is still k whenever j >= k, i.e. for every reserve below 2500 wei with a large remainder. The property only holds for reserves above 2500 wei.
The suite's testFuzz_sellsAlwaysPayAndBidFalls uses assertLe, so it does not detect this. Dust-level, no loss; noted so the README wording ('bids fall geometrically, subject to integer rounding') is kept precise.
State: seed(99) so reserve = 99, bid() = 1.
Input: sell(id).
Actual: seller receives 1 wei, reserve = 98, bid() = 1 (unchanged).
Expected per the brief: bid falls.
Second example: reserve 2499 -> bid 49 -> sell -> reserve 2450 -> bid 49.
- Medium, with proof test. In the two ZTO-input swap cases the hook calls
Audit judge
waits onBuild contract project, Write foundry tests, Manifest, Audit math, Audit permissions, Audit economics, Audit flow- Published
- Deployedto Sepolia