creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "src/contracts/aggregators/ThorchainSimpleAggregatorV5.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// -------------------\n// Aggregator Version: 2.0\n// -------------------\npragma solidity 0.8.22;\n\n// ERC20 Interface\ninterface iERC20 {\n  function balanceOf(address) external view returns (uint256);\n}\n\n// ROUTER Interface\ninterface iROUTER {\n  function depositWithExpiry(\n    address payable vault,\n    address asset,\n    uint amount,\n    string memory memo,\n    uint expiration\n  ) external payable;\n}\n\n// Sushi Interface\ninterface iSWAPROUTER {\n  function swapExactTokensForETH(\n    uint256 amountIn,\n    uint256 amountOutMin,\n    address[] calldata path,\n    address to,\n    uint256 deadline\n  ) external;\n\n  function swapExactETHForTokens(\n    uint amountOutMin,\n    address[] calldata path,\n    address to,\n    uint deadline\n  ) external payable;\n}\n\n// THORChain_Aggregator is permissionless\ncontract ThorchainSimpleAggregatorV5 {\n  uint256 private constant _NOT_ENTERED = 1;\n  uint256 private constant _ENTERED = 2;\n  uint256 private _status;\n\n  address private ETH = address(0);\n  address public WETH;\n  iSWAPROUTER public swapRouter;\n  address public owner;\n\n  modifier nonReentrant() {\n    require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n    _status = _ENTERED;\n    _;\n    _status = _NOT_ENTERED;\n  }\n\n  modifier onlyOwner() {\n    require(msg.sender == owner, \"Caller is not the owner\");\n    _;\n  }\n\n  constructor(address _weth, address _swapRouter) {\n    _status = _NOT_ENTERED;\n    WETH = _weth;\n    swapRouter = iSWAPROUTER(_swapRouter);\n    owner = msg.sender;\n  }\n\n  receive() external payable {}\n\n  function rescueFunds(\n    address asset,\n    uint256 amount,\n    address destination\n  ) public onlyOwner {\n    if (asset == address(0)) {\n      payable(destination).transfer(amount);\n    } else {\n      (bool success, ) = asset.call(\n        abi.encodeWithSignature(\n          \"transfer(address,uint256)\",\n          destination,\n          amount\n        )\n      );\n      require(success, \"Transfer failed\");\n    }\n  }\n\n  //############################## IN ##############################\n\n  function swapIn(\n    address tcVault,\n    address tcRouter,\n    string calldata tcMemo,\n    address token,\n    uint amount,\n    uint amountOutMin,\n    uint256 deadline\n  ) public nonReentrant {\n    uint256 _safeAmount = safeTransferFrom(token, amount); // Transfer asset\n    safeApprove(token, address(swapRouter), amount);\n    address[] memory path = new address[](2);\n    path[0] = token;\n    path[1] = WETH;\n    swapRouter.swapExactTokensForETH(\n      _safeAmount,\n      amountOutMin,\n      path,\n      address(this),\n      deadline\n    );\n    _safeAmount = address(this).balance;\n    iROUTER(tcRouter).depositWithExpiry{value: _safeAmount}(\n      payable(tcVault),\n      ETH,\n      _safeAmount,\n      tcMemo,\n      deadline\n    );\n  }\n\n  //############################## OUT ##############################\n\n  // V4 swapOut kept for backward compatibility\n  function swapOut(\n    address token,\n    address to,\n    uint256 amountOutMin\n  ) public payable nonReentrant {\n    address[] memory path = new address[](2);\n    path[0] = WETH;\n    path[1] = token;\n    swapRouter.swapExactETHForTokens{value: msg.value}(\n      amountOutMin,\n      path,\n      to,\n      type(uint).max\n    );\n  }\n\n  function swapOutV5(\n    address fromAsset,\n    uint256 fromAmount,\n    address toAsset,\n    address recipient,\n    uint256 amountOutMin,\n    bytes memory payload,\n    string memory originAddress\n  ) public payable nonReentrant {\n    address[] memory path = new address[](2);\n\n    // you can do something with originAddress: https://gitlab.com/thorchain/thornode/-/merge_requests/3339#note_1884273837\n\n    if (fromAsset == address(0)) {\n      // received ETH, swap to toToken or do as you please\n      path[0] = WETH;\n      path[1] = toAsset;\n\n      if (payload.length == 0) {\n        // no payload, process without wasting gas\n        swapRouter.swapExactETHForTokens{value: msg.value}(\n          amountOutMin,\n          path,\n          recipient,\n          type(uint).max\n        );\n      } else {\n        // do something with your payload like parse to an aggregator specific structure\n        swapRouter.swapExactETHForTokens{value: msg.value}(\n          amountOutMin,\n          path,\n          recipient,\n          type(uint).max\n        );\n      }\n    } else {\n      // received an ERC20, swap to ETH or do as you please\n      path[0] = fromAsset;\n      path[1] = WETH;\n      safeApprove(fromAsset, address(swapRouter), 0); // best practice\n      safeApprove(fromAsset, address(swapRouter), fromAmount);\n\n      if (payload.length == 0) {\n        // no payload, process without wasting gas\n        // UniV2 transfers from 'msg.sender', so we need a low-level `.call` to change the msg.sender from the target's perspective\n        (bool aggSuccess, ) = address(swapRouter).call(\n          abi.encodeWithSignature(\n            \"swapExactTokensForETH(uint256,uint256,address[],address,uint256)\",\n            fromAmount,\n            amountOutMin,\n            path,\n            recipient,\n            type(uint).max // Assuming using the maximum uint256 value as the deadline\n          )\n        );\n        require(aggSuccess, \"swapExactTokensForETH failed\");\n      } else {\n        // do something with your payload like parse to an aggregator specific structure\n        // UniV2 transfers from 'msg.sender', so we need a low-level `.call` to change the msg.sender from the target's perspective\n        (bool success, ) = address(swapRouter).call(\n          abi.encodeWithSignature(\n            \"swapExactTokensForETH(uint256,uint256,address[],address,uint256)\",\n            fromAmount,\n            amountOutMin,\n            path,\n            recipient,\n            type(uint).max // Assuming using the maximum uint256 value as the deadline\n          )\n        );\n        require(success, \"swapExactTokensForETH failed\");\n      }\n    }\n  }\n\n  //############################## HELPERS ##############################\n\n  // Safe transferFrom in case asset charges transfer fees\n  function safeTransferFrom(\n    address _asset,\n    uint _amount\n  ) internal returns (uint amount) {\n    uint _startBal = iERC20(_asset).balanceOf(address(this));\n    (bool success, bytes memory data) = _asset.call(\n      abi.encodeWithSignature(\n        \"transferFrom(address,address,uint256)\",\n        msg.sender,\n        address(this),\n        _amount\n      )\n    );\n    require(success && (data.length == 0 || abi.decode(data, (bool))));\n    return (iERC20(_asset).balanceOf(address(this)) - _startBal);\n  }\n\n  function safeApprove(\n    address _asset,\n    address _address,\n    uint _amount\n  ) internal {\n    (bool success, ) = _asset.call(\n      abi.encodeWithSignature(\"approve(address,uint256)\", _address, _amount)\n    ); // Approve to transfer\n    require(success);\n  }\n}\n"
      }
    },
    "settings": {
      "viaIR": true,
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "evmVersion": "paris",
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}