{
  "language": "Solidity",
  "settings": {
    "evmVersion": "prague",
    "optimizer": {
      "enabled": true,
      "runs": 500
    },
    "outputSelection": {
      "*": {
        "": [
          "ast"
        ],
        "*": [
          "abi",
          "evm.bytecode.linkReferences",
          "evm.bytecode.object",
          "evm.bytecode.opcodes",
          "evm.bytecode.sourceMap",
          "evm.deployedBytecode.immutableReferences",
          "evm.deployedBytecode.linkReferences",
          "evm.deployedBytecode.object",
          "evm.deployedBytecode.opcodes",
          "evm.deployedBytecode.sourceMap",
          "evm.methodIdentifiers"
        ]
      }
    },
    "remappings": [],
    "viaIR": true
  },
  "sources": {
    "project/contracts/ADEXFactory.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport \"./ADEXPair.sol\";\n\ncontract ADEXFactory {\n    address public feeSetter;\n    uint16 public swapFeeBps = 30;\n\n    mapping(address => mapping(address => address)) public getPair;\n    address[] public allPairs;\n\n    event PairCreated(address indexed token0, address indexed token1, address pair, uint256 pairCount);\n    event SwapFeeUpdated(uint16 previousFeeBps, uint16 newFeeBps);\n    event FeeSetterUpdated(address indexed previousSetter, address indexed newSetter);\n\n    modifier onlyFeeSetter() {\n        require(msg.sender == feeSetter, \"ADEX: FORBIDDEN\");\n        _;\n    }\n\n    constructor(address _feeSetter) {\n        require(_feeSetter != address(0), \"ADEX: ZERO_SETTER\");\n        feeSetter = _feeSetter;\n    }\n\n    function allPairsLength() external view returns (uint256) {\n        return allPairs.length;\n    }\n    function createPair(address tokenA, address tokenB) external returns (address pair) {\n        require(tokenA != tokenB, \"ADEX: IDENTICAL_ADDRESSES\");\n        (address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);\n        require(token0 != address(0), \"ADEX: ZERO_ADDRESS\");\n        require(getPair[token0][token1] == address(0), \"ADEX: PAIR_EXISTS\");\n\n        bytes32 salt = keccak256(abi.encodePacked(token0, token1));\n        pair = address(new ADEXPair{salt: salt}(token0, token1));\n        getPair[token0][token1] = pair;\n        getPair[token1][token0] = pair;\n        allPairs.push(pair);\n        emit PairCreated(token0, token1, pair, allPairs.length);\n    }\n\n    function setSwapFeeBps(uint16 newFeeBps) external onlyFeeSetter {\n        require(newFeeBps <= 100, \"ADEX: FEE_TOO_HIGH\");\n        uint16 previous = swapFeeBps;\n        swapFeeBps = newFeeBps;\n        emit SwapFeeUpdated(previous, newFeeBps);\n    }\n\n    function setFeeSetter(address newSetter) external onlyFeeSetter {\n        require(newSetter != address(0), \"ADEX: ZERO_SETTER\");\n        address previous = feeSetter;\n        feeSetter = newSetter;\n        emit FeeSetterUpdated(previous, newSetter);\n    }\n}\n"
    },
    "project/contracts/ADEXLP.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ncontract ADEXLP {\n    string public constant name = \"ADEX LP\";\n    string public constant symbol = \"ADEX-LP\";\n    uint8 public constant decimals = 18;\n\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    function approve(address spender, uint256 value) external returns (bool) {\n        allowance[msg.sender][spender] = value;\n        emit Approval(msg.sender, spender, value);\n        return true;\n    }\n\n    function transfer(address to, uint256 value) external returns (bool) {\n        _transfer(msg.sender, to, value);\n        return true;\n    }\n    function transferFrom(address from, address to, uint256 value) external returns (bool) {\n        uint256 allowed = allowance[from][msg.sender];\n        if (allowed != type(uint256).max) {\n            require(allowed >= value, \"ADEX: INSUFFICIENT_ALLOWANCE\");\n            allowance[from][msg.sender] = allowed - value;\n        }\n        _transfer(from, to, value);\n        return true;\n    }\n\n    function _transfer(address from, address to, uint256 value) internal {\n        require(to != address(0), \"ADEX: ZERO_TO\");\n        require(balanceOf[from] >= value, \"ADEX: INSUFFICIENT_BALANCE\");\n        unchecked {\n            balanceOf[from] -= value;\n            balanceOf[to] += value;\n        }\n        emit Transfer(from, to, value);\n    }\n\n    function _mint(address to, uint256 value) internal {\n        totalSupply += value;\n        balanceOf[to] += value;\n        emit Transfer(address(0), to, value);\n    }\n    function _burn(address from, uint256 value) internal {\n        require(balanceOf[from] >= value, \"ADEX: INSUFFICIENT_LIQUIDITY\");\n        unchecked {\n            balanceOf[from] -= value;\n            totalSupply -= value;\n        }\n        emit Transfer(from, address(0), value);\n    }\n}\n"
    },
    "project/contracts/ADEXPair.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport \"./ADEXLP.sol\";\nimport \"./IADEXFactory.sol\";\nimport \"./TransferLib.sol\";\n\ncontract ADEXPair is ADEXLP {\n    using TransferLib for address;\n\n    uint256 public constant MINIMUM_LIQUIDITY = 1000;\n    address public immutable factory;\n    address public immutable token0;\n    address public immutable token1;\n\n    uint112 private reserve0;\n    uint112 private reserve1;\n    uint32 private blockTimestampLast;\n    uint256 private unlocked = 1;\n\n    event Mint(address indexed sender, uint256 amount0, uint256 amount1);\n    event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);\n    event Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to);\n    event Sync(uint112 reserve0, uint112 reserve1);\n\n    modifier lock() {\n        require(unlocked == 1, \"ADEX: LOCKED\");\n        unlocked = 0;\n        _;\n        unlocked = 1;\n    }\n    constructor(address _token0, address _token1) {\n        require(_token0 != address(0) && _token1 != address(0), \"ADEX: ZERO_TOKEN\");\n        factory = msg.sender;\n        token0 = _token0;\n        token1 = _token1;\n    }\n\n    function getReserves() external view returns (uint112, uint112, uint32) {\n        return (reserve0, reserve1, blockTimestampLast);\n    }\n\n    function mint(address to) external lock returns (uint256 liquidity) {\n        (uint112 _reserve0, uint112 _reserve1,) = this.getReserves();\n        uint256 balance0 = IERC20Minimal(token0).balanceOf(address(this));\n        uint256 balance1 = IERC20Minimal(token1).balanceOf(address(this));\n        uint256 amount0 = balance0 - _reserve0;\n        uint256 amount1 = balance1 - _reserve1;\n\n        if (totalSupply == 0) {\n            uint256 root = _sqrt(amount0 * amount1);\n            require(root > MINIMUM_LIQUIDITY, \"ADEX: INSUFFICIENT_INITIAL_LIQUIDITY\");\n            liquidity = root - MINIMUM_LIQUIDITY;\n            _mint(address(0), MINIMUM_LIQUIDITY);\n        } else {\n            liquidity = _min(amount0 * totalSupply / _reserve0, amount1 * totalSupply / _reserve1);\n        }\n        require(liquidity > 0, \"ADEX: INSUFFICIENT_LIQUIDITY_MINTED\");\n        _mint(to, liquidity);\n        _update(balance0, balance1);\n        emit Mint(msg.sender, amount0, amount1);\n    }\n    function burn(address to) external lock returns (uint256 amount0, uint256 amount1) {\n        uint256 balance0 = IERC20Minimal(token0).balanceOf(address(this));\n        uint256 balance1 = IERC20Minimal(token1).balanceOf(address(this));\n        uint256 liquidity = balanceOf[address(this)];\n        uint256 supply = totalSupply;\n\n        amount0 = liquidity * balance0 / supply;\n        amount1 = liquidity * balance1 / supply;\n        require(amount0 > 0 && amount1 > 0, \"ADEX: INSUFFICIENT_LIQUIDITY_BURNED\");\n\n        _burn(address(this), liquidity);\n        token0.safeTransfer(to, amount0);\n        token1.safeTransfer(to, amount1);\n\n        balance0 = IERC20Minimal(token0).balanceOf(address(this));\n        balance1 = IERC20Minimal(token1).balanceOf(address(this));\n        _update(balance0, balance1);\n        emit Burn(msg.sender, amount0, amount1, to);\n    }\n\n    function swap(uint256 amount0Out, uint256 amount1Out, address to) external lock {\n        require(amount0Out > 0 || amount1Out > 0, \"ADEX: INSUFFICIENT_OUTPUT\");\n        (uint112 _reserve0, uint112 _reserve1,) = this.getReserves();\n        require(amount0Out < _reserve0 && amount1Out < _reserve1, \"ADEX: INSUFFICIENT_LIQUIDITY\");\n        require(to != token0 && to != token1, \"ADEX: INVALID_TO\");\n\n        if (amount0Out > 0) token0.safeTransfer(to, amount0Out);\n        if (amount1Out > 0) token1.safeTransfer(to, amount1Out);\n        uint256 balance0 = IERC20Minimal(token0).balanceOf(address(this));\n        uint256 balance1 = IERC20Minimal(token1).balanceOf(address(this));\n        uint256 amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;\n        uint256 amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;\n        require(amount0In > 0 || amount1In > 0, \"ADEX: INSUFFICIENT_INPUT\");\n\n        uint256 feeBps = IADEXFactory(factory).swapFeeBps();\n        uint256 balance0Adjusted = balance0 * 10_000 - amount0In * feeBps;\n        uint256 balance1Adjusted = balance1 * 10_000 - amount1In * feeBps;\n        require(\n            balance0Adjusted * balance1Adjusted >= uint256(_reserve0) * uint256(_reserve1) * 100_000_000,\n            \"ADEX: K\"\n        );\n\n        _update(balance0, balance1);\n        emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);\n    }\n\n    function skim(address to) external lock {\n        token0.safeTransfer(to, IERC20Minimal(token0).balanceOf(address(this)) - reserve0);\n        token1.safeTransfer(to, IERC20Minimal(token1).balanceOf(address(this)) - reserve1);\n    }\n\n    function sync() external lock {\n        _update(IERC20Minimal(token0).balanceOf(address(this)), IERC20Minimal(token1).balanceOf(address(this)));\n    }\n    function _update(uint256 balance0, uint256 balance1) private {\n        require(balance0 <= type(uint112).max && balance1 <= type(uint112).max, \"ADEX: OVERFLOW\");\n        reserve0 = uint112(balance0);\n        reserve1 = uint112(balance1);\n        blockTimestampLast = uint32(block.timestamp);\n        emit Sync(reserve0, reserve1);\n    }\n\n    function _min(uint256 x, uint256 y) private pure returns (uint256) {\n        return x < y ? x : y;\n    }\n\n    function _sqrt(uint256 y) private pure returns (uint256 z) {\n        if (y > 3) {\n            z = y;\n            uint256 x = y / 2 + 1;\n            while (x < z) {\n                z = x;\n                x = (y / x + x) / 2;\n            }\n        } else if (y != 0) {\n            z = 1;\n        }\n    }\n}\n"
    },
    "project/contracts/ADEXRouter.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport \"./IADEXFactory.sol\";\nimport \"./IADEXPair.sol\";\nimport \"./TransferLib.sol\";\n\ncontract ADEXRouter {\n    using TransferLib for address;\n\n    address public immutable factory;\n\n    modifier ensure(uint256 deadline) {\n        require(deadline >= block.timestamp, \"ADEX: EXPIRED\");\n        _;\n    }\n\n    constructor(address _factory) {\n        require(_factory != address(0), \"ADEX: ZERO_FACTORY\");\n        factory = _factory;\n    }\n\n    function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) public pure returns (uint256 amountB) {\n        require(amountA > 0, \"ADEX: INSUFFICIENT_AMOUNT\");\n        require(reserveA > 0 && reserveB > 0, \"ADEX: INSUFFICIENT_LIQUIDITY\");\n        amountB = amountA * reserveB / reserveA;\n    }\n\n    function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) public view returns (uint256 amountOut) {\n        require(amountIn > 0, \"ADEX: INSUFFICIENT_INPUT\");\n        require(reserveIn > 0 && reserveOut > 0, \"ADEX: INSUFFICIENT_LIQUIDITY\");\n        uint256 feeBps = IADEXFactory(factory).swapFeeBps();\n        uint256 amountInWithFee = amountIn * (10_000 - feeBps);\n        amountOut = amountInWithFee * reserveOut / (reserveIn * 10_000 + amountInWithFee);\n    }\n    function getAmountsOut(uint256 amountIn, address[] memory path) public view returns (uint256[] memory amounts) {\n        require(path.length >= 2, \"ADEX: INVALID_PATH\");\n        amounts = new uint256[](path.length);\n        amounts[0] = amountIn;\n        for (uint256 i; i < path.length - 1; ++i) {\n            (uint256 reserveIn, uint256 reserveOut) = _getReserves(path[i], path[i + 1]);\n            amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);\n        }\n    }\n\n    function addLiquidity(\n        address tokenA,\n        address tokenB,\n        uint256 amountADesired,\n        uint256 amountBDesired,\n        uint256 amountAMin,\n        uint256 amountBMin,\n        address to,\n        uint256 deadline\n    ) external ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) {\n        (amountA, amountB) = _addLiquidity(\n            tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin\n        );\n        address pair = IADEXFactory(factory).getPair(tokenA, tokenB);\n        tokenA.safeTransferFrom(msg.sender, pair, amountA);\n        tokenB.safeTransferFrom(msg.sender, pair, amountB);\n        liquidity = IADEXPair(pair).mint(to);\n    }\n    function removeLiquidity(\n        address tokenA,\n        address tokenB,\n        uint256 liquidity,\n        uint256 amountAMin,\n        uint256 amountBMin,\n        address to,\n        uint256 deadline\n    ) external ensure(deadline) returns (uint256 amountA, uint256 amountB) {\n        address pair = IADEXFactory(factory).getPair(tokenA, tokenB);\n        require(pair != address(0), \"ADEX: PAIR_NOT_FOUND\");\n        require(IADEXPair(pair).transferFrom(msg.sender, pair, liquidity), \"ADEX: LP_TRANSFER_FAILED\");\n        (uint256 amount0, uint256 amount1) = IADEXPair(pair).burn(to);\n        (address token0,) = _sortTokens(tokenA, tokenB);\n        (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);\n        require(amountA >= amountAMin, \"ADEX: INSUFFICIENT_A_AMOUNT\");\n        require(amountB >= amountBMin, \"ADEX: INSUFFICIENT_B_AMOUNT\");\n    }\n\n    function swapExactTokensForTokens(\n        uint256 amountIn,\n        uint256 amountOutMin,\n        address[] calldata path,\n        address to,\n        uint256 deadline\n    ) external ensure(deadline) returns (uint256[] memory amounts) {\n        amounts = getAmountsOut(amountIn, path);\n        require(amounts[amounts.length - 1] >= amountOutMin, \"ADEX: INSUFFICIENT_OUTPUT\");\n        address firstPair = IADEXFactory(factory).getPair(path[0], path[1]);\n        require(firstPair != address(0), \"ADEX: PAIR_NOT_FOUND\");\n        path[0].safeTransferFrom(msg.sender, firstPair, amounts[0]);\n        _swap(amounts, path, to);\n    }\n    function _addLiquidity(\n        address tokenA,\n        address tokenB,\n        uint256 amountADesired,\n        uint256 amountBDesired,\n        uint256 amountAMin,\n        uint256 amountBMin\n    ) private returns (uint256 amountA, uint256 amountB) {\n        if (IADEXFactory(factory).getPair(tokenA, tokenB) == address(0)) {\n            IADEXFactory(factory).createPair(tokenA, tokenB);\n        }\n        (uint256 reserveA, uint256 reserveB) = _getReserves(tokenA, tokenB);\n        if (reserveA == 0 && reserveB == 0) {\n            (amountA, amountB) = (amountADesired, amountBDesired);\n        } else {\n            uint256 amountBOptimal = quote(amountADesired, reserveA, reserveB);\n            if (amountBOptimal <= amountBDesired) {\n                require(amountBOptimal >= amountBMin, \"ADEX: INSUFFICIENT_B_AMOUNT\");\n                (amountA, amountB) = (amountADesired, amountBOptimal);\n            } else {\n                uint256 amountAOptimal = quote(amountBDesired, reserveB, reserveA);\n                require(amountAOptimal <= amountADesired, \"ADEX: INVALID_OPTIMAL_AMOUNT\");\n                require(amountAOptimal >= amountAMin, \"ADEX: INSUFFICIENT_A_AMOUNT\");\n                (amountA, amountB) = (amountAOptimal, amountBDesired);\n            }\n        }\n    }\n    function _swap(uint256[] memory amounts, address[] calldata path, address finalTo) private {\n        for (uint256 i; i < path.length - 1; ++i) {\n            (address input, address output) = (path[i], path[i + 1]);\n            (address token0,) = _sortTokens(input, output);\n            uint256 amountOut = amounts[i + 1];\n            (uint256 amount0Out, uint256 amount1Out) =\n                input == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));\n            address to = i < path.length - 2\n                ? IADEXFactory(factory).getPair(output, path[i + 2])\n                : finalTo;\n            address pair = IADEXFactory(factory).getPair(input, output);\n            require(pair != address(0) && to != address(0), \"ADEX: INVALID_ROUTE\");\n            IADEXPair(pair).swap(amount0Out, amount1Out, to);\n        }\n    }\n\n    function _getReserves(address tokenA, address tokenB) private view returns (uint256 reserveA, uint256 reserveB) {\n        address pair = IADEXFactory(factory).getPair(tokenA, tokenB);\n        require(pair != address(0), \"ADEX: PAIR_NOT_FOUND\");\n        (uint112 reserve0, uint112 reserve1,) = IADEXPair(pair).getReserves();\n        (address token0,) = _sortTokens(tokenA, tokenB);\n        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);\n    }\n    function _sortTokens(address tokenA, address tokenB) private pure returns (address token0, address token1) {\n        require(tokenA != tokenB, \"ADEX: IDENTICAL_ADDRESSES\");\n        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);\n        require(token0 != address(0), \"ADEX: ZERO_ADDRESS\");\n    }\n}\n"
    },
    "project/contracts/IADEXFactory.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ninterface IADEXFactory {\n    function getPair(address tokenA, address tokenB) external view returns (address pair);\n    function createPair(address tokenA, address tokenB) external returns (address pair);\n    function swapFeeBps() external view returns (uint16);\n}\n"
    },
    "project/contracts/IADEXPair.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ninterface IADEXPair {\n    function token0() external view returns (address);\n    function token1() external view returns (address);\n    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);\n    function mint(address to) external returns (uint256 liquidity);\n    function burn(address to) external returns (uint256 amount0, uint256 amount1);\n    function swap(uint256 amount0Out, uint256 amount1Out, address to) external;\n    function sync() external;\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n"
    },
    "project/contracts/test/MockERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ncontract MockERC20 {\n    string public name;\n    string public symbol;\n    uint8 public constant decimals = 18;\n    uint256 public totalSupply;\n\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    event Transfer(address indexed from, address indexed to, uint256 value);\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    constructor(string memory _name, string memory _symbol, uint256 supply) {\n        name = _name;\n        symbol = _symbol;\n        _mint(msg.sender, supply);\n    }\n\n    function approve(address spender, uint256 value) external returns (bool) {\n        allowance[msg.sender][spender] = value;\n        emit Approval(msg.sender, spender, value);\n        return true;\n    }\n    function transfer(address to, uint256 value) external returns (bool) {\n        _transfer(msg.sender, to, value);\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 value) external returns (bool) {\n        uint256 allowed = allowance[from][msg.sender];\n        if (allowed != type(uint256).max) {\n            require(allowed >= value, \"MOCK: ALLOWANCE\");\n            allowance[from][msg.sender] = allowed - value;\n        }\n        _transfer(from, to, value);\n        return true;\n    }\n\n    function mint(address to, uint256 value) external {\n        _mint(to, value);\n    }\n\n    function _transfer(address from, address to, uint256 value) private {\n        require(to != address(0), \"MOCK: ZERO_TO\");\n        require(balanceOf[from] >= value, \"MOCK: BALANCE\");\n        unchecked {\n            balanceOf[from] -= value;\n            balanceOf[to] += value;\n        }\n        emit Transfer(from, to, value);\n    }\n    function _mint(address to, uint256 value) private {\n        require(to != address(0), \"MOCK: ZERO_TO\");\n        totalSupply += value;\n        balanceOf[to] += value;\n        emit Transfer(address(0), to, value);\n    }\n}\n"
    },
    "project/contracts/TransferLib.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ninterface IERC20Minimal {\n    function balanceOf(address account) external view returns (uint256);\n    function transfer(address to, uint256 value) external returns (bool);\n    function transferFrom(address from, address to, uint256 value) external returns (bool);\n}\n\nlibrary TransferLib {\n    function safeTransfer(address token, address to, uint256 value) internal {\n        (bool ok, bytes memory data) = token.call(\n            abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value)\n        );\n        require(ok && (data.length == 0 || abi.decode(data, (bool))), \"ADEX: TRANSFER_FAILED\");\n    }\n\n    function safeTransferFrom(address token, address from, address to, uint256 value) internal {\n        (bool ok, bytes memory data) = token.call(\n            abi.encodeWithSelector(IERC20Minimal.transferFrom.selector, from, to, value)\n        );\n        require(ok && (data.length == 0 || abi.decode(data, (bool))), \"ADEX: TRANSFER_FROM_FAILED\");\n    }\n}\n"
    }
  }
}
