{
  "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/ADEXRouter.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport \"./IADEXFactory.sol\";\nimport \"./IADEXPair.sol\";\nimport \"./TransferLib.sol\";\nimport \"./IWETH.sol\";\n\ncontract ADEXRouter {\n    using TransferLib for address;\n\n    address public immutable factory;\n    address public immutable wrappedNative;\n\n    modifier ensure(uint256 deadline) {\n        require(deadline >= block.timestamp, \"ADEX: EXPIRED\");\n        _;\n    }\n\n    constructor(address _factory, address _wrappedNative) {\n        require(_factory != address(0), \"ADEX: ZERO_FACTORY\");\n        require(_wrappedNative != address(0), \"ADEX: ZERO_WRAPPED_NATIVE\");\n        factory = _factory;\n        wrappedNative = _wrappedNative;\n    }\n\n    receive() external payable {\n        require(msg.sender == wrappedNative, \"ADEX: NATIVE_ONLY_WRAPPED\");\n    }\n\n    function quote(uint256 amountA, uint256 reserveA, uint256 reserveB)\n        public pure returns (uint256 amountB)\n    {\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)\n        public view returns (uint256 amountOut)\n    {\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\n    function getAmountsOut(uint256 amountIn, address[] memory path)\n        public view returns (uint256[] memory amounts)\n    {\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        require(to != address(0), \"ADEX: ZERO_TO\");\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\n    function addLiquidityETH(\n        address token,\n        uint256 amountTokenDesired,\n        uint256 amountTokenMin,\n        uint256 amountETHMin,\n        address to,\n        uint256 deadline\n    ) external payable ensure(deadline)\n        returns (uint256 amountToken, uint256 amountETH, uint256 liquidity)\n    {\n        require(to != address(0), \"ADEX: ZERO_TO\");\n        require(msg.value > 0, \"ADEX: INSUFFICIENT_NATIVE\");\n        (amountToken, amountETH) = _addLiquidity(\n            token, wrappedNative, amountTokenDesired, msg.value, amountTokenMin, amountETHMin\n        );\n\n        address pair = IADEXFactory(factory).getPair(token, wrappedNative);\n        token.safeTransferFrom(msg.sender, pair, amountToken);\n        IWETH(wrappedNative).deposit{value: amountETH}();\n        wrappedNative.safeTransfer(pair, amountETH);\n        liquidity = IADEXPair(pair).mint(to);\n\n        if (msg.value > amountETH) {\n            _safeTransferETH(msg.sender, msg.value - amountETH);\n        }\n    }\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        require(to != address(0), \"ADEX: ZERO_TO\");\n        return _removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to);\n    }\n\n    function removeLiquidityETH(\n        address token,\n        uint256 liquidity,\n        uint256 amountTokenMin,\n        uint256 amountETHMin,\n        address to,\n        uint256 deadline\n    ) external ensure(deadline) returns (uint256 amountToken, uint256 amountETH) {\n        require(to != address(0), \"ADEX: ZERO_TO\");\n        (amountToken, amountETH) = _removeLiquidity(\n            token, wrappedNative, liquidity, amountTokenMin, amountETHMin, address(this)\n        );\n        token.safeTransfer(to, amountToken);\n        IWETH(wrappedNative).withdraw(amountETH);\n        _safeTransferETH(to, amountETH);\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        require(to != address(0), \"ADEX: ZERO_TO\");\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\n    function swapExactETHForTokens(\n        uint256 amountOutMin,\n        address[] calldata path,\n        address to,\n        uint256 deadline\n    ) external payable ensure(deadline) returns (uint256[] memory amounts) {\n        require(to != address(0), \"ADEX: ZERO_TO\");\n        require(path.length >= 2 && path[0] == wrappedNative, \"ADEX: INVALID_NATIVE_PATH\");\n        amounts = getAmountsOut(msg.value, path);\n        require(amounts[amounts.length - 1] >= amountOutMin, \"ADEX: INSUFFICIENT_OUTPUT\");\n\n        address firstPair = IADEXFactory(factory).getPair(path[0], path[1]);\n        require(firstPair != address(0), \"ADEX: PAIR_NOT_FOUND\");\n        IWETH(wrappedNative).deposit{value: amounts[0]}();\n        wrappedNative.safeTransfer(firstPair, amounts[0]);\n        _swap(amounts, path, to);\n    }\n\n    function swapExactTokensForETH(\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        require(to != address(0), \"ADEX: ZERO_TO\");\n        require(\n            path.length >= 2 && path[path.length - 1] == wrappedNative,\n            \"ADEX: INVALID_NATIVE_PATH\"\n        );\n        amounts = getAmountsOut(amountIn, path);\n        require(amounts[amounts.length - 1] >= amountOutMin, \"ADEX: INSUFFICIENT_OUTPUT\");\n\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, address(this));\n\n        uint256 nativeOut = amounts[amounts.length - 1];\n        IWETH(wrappedNative).withdraw(nativeOut);\n        _safeTransferETH(to, nativeOut);\n    }\n\n    function _removeLiquidity(\n        address tokenA,\n        address tokenB,\n        uint256 liquidity,\n        uint256 amountAMin,\n        uint256 amountBMin,\n        address to\n    ) private returns (uint256 amountA, uint256 amountB) {\n        address pair = IADEXFactory(factory).getPair(tokenA, tokenB);\n        require(pair != address(0), \"ADEX: PAIR_NOT_FOUND\");\n        require(\n            IADEXPair(pair).transferFrom(msg.sender, pair, liquidity),\n            \"ADEX: LP_TRANSFER_FAILED\"\n        );\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 _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        require(amountADesired > 0 && amountBDesired > 0, \"ADEX: INSUFFICIENT_DESIRED_AMOUNT\");\n        require(amountADesired >= amountAMin, \"ADEX: INVALID_A_MIN\");\n        require(amountBDesired >= amountBMin, \"ADEX: INVALID_B_MIN\");\n\n        if (IADEXFactory(factory).getPair(tokenA, tokenB) == address(0)) {\n            IADEXFactory(factory).createPair(tokenA, tokenB);\n        }\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\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\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)\n        private view returns (uint256 reserveA, uint256 reserveB)\n    {\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\n    function _sortTokens(address tokenA, address tokenB)\n        private pure returns (address token0, address token1)\n    {\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    function _safeTransferETH(address to, uint256 value) private {\n        (bool ok,) = to.call{value: value}(\"\");\n        require(ok, \"ADEX: NATIVE_TRANSFER_FAILED\");\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/IWETH.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ninterface IWETH {\n    function deposit() external payable;\n    function withdraw(uint256 amount) external;\n}\n"
    },
    "project/contracts/test/MockWETH.sol": {
      "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\ncontract MockWETH {\n    string public constant name = \"Wrapped Ether\";\n    string public constant symbol = \"WETH\";\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 Deposit(address indexed dst, uint256 wad);\n    event Withdrawal(address indexed src, uint256 wad);\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    receive() external payable {\n        deposit();\n    }\n\n    function deposit() public payable {\n        balanceOf[msg.sender] += msg.value;\n        totalSupply += msg.value;\n        emit Deposit(msg.sender, msg.value);\n        emit Transfer(address(0), msg.sender, msg.value);\n    }\n\n    function withdraw(uint256 wad) external {\n        require(balanceOf[msg.sender] >= wad, \"WETH: BALANCE\");\n        unchecked {\n            balanceOf[msg.sender] -= wad;\n            totalSupply -= wad;\n        }\n        emit Transfer(msg.sender, address(0), wad);\n        emit Withdrawal(msg.sender, wad);\n        (bool ok,) = msg.sender.call{value: wad}(\"\");\n        require(ok, \"WETH: ETH_TRANSFER_FAILED\");\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\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, \"WETH: 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) private {\n        require(to != address(0), \"WETH: ZERO_TO\");\n        require(balanceOf[from] >= value, \"WETH: BALANCE\");\n        unchecked {\n            balanceOf[from] -= value;\n            balanceOf[to] += value;\n        }\n        emit Transfer(from, 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"
    }
  }
}
