{"language":"Solidity","sources":{"src/SashimiV3Factory.sol":{"content":"// SPDX-License-Identifier: GPL-2.0-or-later\npragma solidity 0.8.28;\n\n/// @title SashimiV3Factory\n/// @notice A Uniswap V3 factory that deploys BYTE-IDENTICAL canonical `UniswapV3Pool` contracts\n///         (v3-core 1.0.1 init code — hash 0xe34f…b54, the constant every official periphery\n///         contract derives pool addresses from) but lets exactly ONE address, the Sashimi\n///         graduator, create pools.\n///\n///         Why it exists: a permissionless factory lets anyone pre-create a token's pool, move its\n///         empty price for free and park one-sided orders on the path a graduation would have to\n///         travel. Sashimi therefore never graduates into a pool that existed before the graduating\n///         transaction — and this factory guarantees such a pool always exists, because nobody else\n///         can create one here. Everything else (swaps, positions, fee collection, the position\n///         manager, router and quoter deployed against it) is stock Uniswap V3.\n///\n///         Storage of the pool init code follows the SSTORE2 pattern: it lives as the runtime\n///         bytecode of a helper contract (prefixed with STOP so it can never execute) and is read\n///         back with a single EXTCODECOPY at pool creation.\ncontract SashimiV3Factory {\n    struct Parameters {\n        address factory;\n        address token0;\n        address token1;\n        uint24 fee;\n        int24 tickSpacing;\n    }\n\n    /// @notice keccak256 of the canonical UniswapV3Pool creation code (v3-core 1.0.1).\n    bytes32 public constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;\n\n    address public owner;\n    address public poolCreator;\n    address public immutable poolCodeStore;\n    /// @dev Read by the pool constructor through `IUniswapV3PoolDeployer(msg.sender).parameters()`.\n    Parameters public parameters;\n    mapping(uint24 fee => int24 tickSpacing) public feeAmountTickSpacing;\n    mapping(address => mapping(address => mapping(uint24 => address))) public getPool;\n\n    event OwnerChanged(address indexed oldOwner, address indexed newOwner);\n    event PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool);\n    event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);\n    event PoolCreatorSet(address indexed poolCreator);\n\n    error OnlyOwner();\n    error OnlyPoolCreator();\n    error AlreadySet();\n    error ZeroAddress();\n    error BadPoolCode();\n    error IdenticalTokens();\n    error FeeNotEnabled();\n    error InvalidFee();\n    error PoolExists();\n\n    modifier onlyOwner() {\n        if (msg.sender != owner) revert OnlyOwner();\n        _;\n    }\n\n    constructor(bytes memory poolInitCode, address owner_) {\n        if (keccak256(poolInitCode) != POOL_INIT_CODE_HASH) revert BadPoolCode();\n        if (owner_ == address(0)) revert ZeroAddress();\n        // creation code: PUSH1 0x0b RETURNDATASIZE DUP2 CODESIZE SUB DUP1 SWAP3 RETURNDATASIZE CODECOPY RETURN\n        // → returns everything after the 11-byte prefix as runtime code (STOP + pool init code).\n        bytes memory creation = abi.encodePacked(hex\"600B5981380380925939F3\", hex\"00\", poolInitCode);\n        address store;\n        assembly (\"memory-safe\") {\n            store := create(0, add(creation, 32), mload(creation))\n        }\n        if (store == address(0)) revert BadPoolCode();\n        poolCodeStore = store;\n        owner = owner_;\n        emit OwnerChanged(address(0), owner_);\n        feeAmountTickSpacing[500] = 10;\n        emit FeeAmountEnabled(500, 10);\n        feeAmountTickSpacing[3000] = 60;\n        emit FeeAmountEnabled(3000, 60);\n        feeAmountTickSpacing[10000] = 200;\n        emit FeeAmountEnabled(10000, 200);\n    }\n\n    /// @notice One-shot: the only address allowed to create pools.\n    function setPoolCreator(address creator) external onlyOwner {\n        if (poolCreator != address(0)) revert AlreadySet();\n        if (creator == address(0)) revert ZeroAddress();\n        poolCreator = creator;\n        emit PoolCreatorSet(creator);\n    }\n\n    /// @notice Same semantics as the canonical factory (the pool's `onlyFactoryOwner` reads this).\n    ///         Setting it to address(0) permanently disables fee-tier changes and protocol fees.\n    function setOwner(address newOwner) external onlyOwner {\n        emit OwnerChanged(owner, newOwner);\n        owner = newOwner;\n    }\n\n    function enableFeeAmount(uint24 fee, int24 tickSpacing) external onlyOwner {\n        if (fee >= 1_000_000) revert InvalidFee();\n        if (tickSpacing <= 0 || tickSpacing >= 16384) revert InvalidFee();\n        if (feeAmountTickSpacing[fee] != 0) revert AlreadySet();\n        feeAmountTickSpacing[fee] = tickSpacing;\n        emit FeeAmountEnabled(fee, tickSpacing);\n    }\n\n    /// @notice Deploy a canonical pool. Pool-creator only.\n    function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool) {\n        if (msg.sender != poolCreator) revert OnlyPoolCreator();\n        if (tokenA == tokenB) revert IdenticalTokens();\n        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);\n        if (token0 == address(0)) revert ZeroAddress();\n        int24 tickSpacing = feeAmountTickSpacing[fee];\n        if (tickSpacing == 0) revert FeeNotEnabled();\n        if (getPool[token0][token1][fee] != address(0)) revert PoolExists();\n\n        parameters = Parameters({factory: address(this), token0: token0, token1: token1, fee: fee, tickSpacing: tickSpacing});\n        bytes memory code = poolInitCode();\n        bytes32 salt = keccak256(abi.encode(token0, token1, fee));\n        assembly (\"memory-safe\") {\n            pool := create2(0, add(code, 32), mload(code), salt)\n        }\n        delete parameters;\n        if (pool == address(0)) revert BadPoolCode();\n\n        getPool[token0][token1][fee] = pool;\n        getPool[token1][token0][fee] = pool;\n        emit PoolCreated(token0, token1, fee, tickSpacing, pool);\n    }\n\n    /// @notice The canonical pool creation code this factory deploys.\n    function poolInitCode() public view returns (bytes memory code) {\n        address store = poolCodeStore;\n        uint256 size;\n        assembly (\"memory-safe\") {\n            size := sub(extcodesize(store), 1)\n        }\n        code = new bytes(size);\n        assembly (\"memory-safe\") {\n            extcodecopy(store, add(code, 32), 1, size)\n        }\n    }\n}\n"}},"settings":{"remappings":["@openzeppelin/=lib/openzeppelin-contracts/","ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"outputSelection":{"*":{"":["ast"],"*":["abi","evm.bytecode.object","evm.bytecode.sourceMap","evm.bytecode.linkReferences","evm.deployedBytecode.object","evm.deployedBytecode.sourceMap","evm.deployedBytecode.linkReferences","evm.deployedBytecode.immutableReferences","evm.methodIdentifiers","metadata"]}},"evmVersion":"cancun","viaIR":true,"libraries":{}}}
