github.com/diadata-org/diadata@v1.4.593/pkg/dia/scraper/exchange-scrapers/anyswap/anyswaproutereth.sol (about)

     1  /**
     2   *Submitted for verification at Etherscan.io on 2021-06-18
     3  */
     4  
     5  /**
     6   *Submitted for verification at FtmScan.com on 2021-05-31
     7  */
     8  
     9  /**
    10   *Submitted for verification at BscScan.com on 2021-04-15
    11  */
    12  
    13  /**
    14   *Submitted for verification at BscScan.com on 2021-04-08
    15  */
    16  
    17  /**
    18   *Submitted for verification at hecoinfo.com on 2021-04-08
    19  */
    20  
    21  // SPDX-License-Identifier: GPL-3.0-or-later
    22  
    23  pragma solidity >=0.8.0;
    24  
    25  interface ISushiswapV2Pair {
    26      function factory() external view returns (address);
    27      function token0() external view returns (address);
    28      function token1() external view returns (address);
    29      function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    30      function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    31  }
    32  
    33  // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
    34  
    35  library SafeMathSushiswap {
    36      function add(uint x, uint y) internal pure returns (uint z) {
    37          require((z = x + y) >= x, 'ds-math-add-overflow');
    38      }
    39  
    40      function sub(uint x, uint y) internal pure returns (uint z) {
    41          require((z = x - y) <= x, 'ds-math-sub-underflow');
    42      }
    43  
    44      function mul(uint x, uint y) internal pure returns (uint z) {
    45          require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
    46      }
    47  }
    48  
    49  library SushiswapV2Library {
    50      using SafeMathSushiswap for uint;
    51  
    52      // returns sorted token addresses, used to handle return values from pairs sorted in this order
    53      function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
    54          require(tokenA != tokenB, 'SushiswapV2Library: IDENTICAL_ADDRESSES');
    55          (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
    56          require(token0 != address(0), 'SushiswapV2Library: ZERO_ADDRESS');
    57      }
    58  
    59      // calculates the CREATE2 address for a pair without making any external calls
    60      function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
    61          (address token0, address token1) = sortTokens(tokenA, tokenB);
    62          pair = address(uint160(uint256(keccak256(abi.encodePacked(
    63                  hex'ff',
    64                  factory,
    65                  keccak256(abi.encodePacked(token0, token1)),
    66                  hex'e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303' // init code hash
    67              )))));
    68      }
    69  
    70      // fetches and sorts the reserves for a pair
    71      function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
    72          (address token0,) = sortTokens(tokenA, tokenB);
    73          (uint reserve0, uint reserve1,) = ISushiswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
    74          (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    75      }
    76  
    77      // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
    78      function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
    79          require(amountA > 0, 'SushiswapV2Library: INSUFFICIENT_AMOUNT');
    80          require(reserveA > 0 && reserveB > 0, 'SushiswapV2Library: INSUFFICIENT_LIQUIDITY');
    81          amountB = amountA.mul(reserveB) / reserveA;
    82      }
    83  
    84      // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
    85      function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
    86          require(amountIn > 0, 'SushiswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
    87          require(reserveIn > 0 && reserveOut > 0, 'SushiswapV2Library: INSUFFICIENT_LIQUIDITY');
    88          uint amountInWithFee = amountIn.mul(997);
    89          uint numerator = amountInWithFee.mul(reserveOut);
    90          uint denominator = reserveIn.mul(1000).add(amountInWithFee);
    91          amountOut = numerator / denominator;
    92      }
    93  
    94      // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
    95      function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
    96          require(amountOut > 0, 'SushiswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
    97          require(reserveIn > 0 && reserveOut > 0, 'SushiswapV2Library: INSUFFICIENT_LIQUIDITY');
    98          uint numerator = reserveIn.mul(amountOut).mul(1000);
    99          uint denominator = reserveOut.sub(amountOut).mul(997);
   100          amountIn = (numerator / denominator).add(1);
   101      }
   102  
   103      // performs chained getAmountOut calculations on any number of pairs
   104      function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
   105          require(path.length >= 2, 'SushiswapV2Library: INVALID_PATH');
   106          amounts = new uint[](path.length);
   107          amounts[0] = amountIn;
   108          for (uint i; i < path.length - 1; i++) {
   109              (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
   110              amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
   111          }
   112      }
   113  
   114      // performs chained getAmountIn calculations on any number of pairs
   115      function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
   116          require(path.length >= 2, 'SushiswapV2Library: INVALID_PATH');
   117          amounts = new uint[](path.length);
   118          amounts[amounts.length - 1] = amountOut;
   119          for (uint i = path.length - 1; i > 0; i--) {
   120              (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
   121              amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
   122          }
   123      }
   124  }
   125  
   126  // helper methods for interacting with ERC20 tokens and sending NATIVE that do not consistently return true/false
   127  library TransferHelper {
   128      function safeApprove(address token, address to, uint value) internal {
   129          // bytes4(keccak256(bytes('approve(address,uint256)')));
   130          (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
   131          require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
   132      }
   133  
   134      function safeTransfer(address token, address to, uint value) internal {
   135          // bytes4(keccak256(bytes('transfer(address,uint256)')));
   136          (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
   137          require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
   138      }
   139  
   140      function safeTransferFrom(address token, address from, address to, uint value) internal {
   141          // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
   142          (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
   143          require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
   144      }
   145  
   146      function safeTransferNative(address to, uint value) internal {
   147          (bool success,) = to.call{value:value}(new bytes(0));
   148          require(success, 'TransferHelper: NATIVE_TRANSFER_FAILED');
   149      }
   150  }
   151  
   152  interface IwNATIVE {
   153      function deposit() external payable;
   154      function transfer(address to, uint value) external returns (bool);
   155      function withdraw(uint) external;
   156  }
   157  
   158  interface AnyswapV1ERC20 {
   159      function mint(address to, uint256 amount) external returns (bool);
   160      function burn(address from, uint256 amount) external returns (bool);
   161      function changeVault(address newVault) external returns (bool);
   162      function depositVault(uint amount, address to) external returns (uint);
   163      function withdrawVault(address from, uint amount, address to) external returns (uint);
   164      function underlying() external view returns (address);
   165  }
   166  
   167  /**
   168   * @dev Interface of the ERC20 standard as defined in the EIP.
   169   */
   170  interface IERC20 {
   171      function totalSupply() external view returns (uint256);
   172      function balanceOf(address account) external view returns (uint256);
   173      function transfer(address recipient, uint256 amount) external returns (bool);
   174      function allowance(address owner, address spender) external view returns (uint256);
   175      function approve(address spender, uint256 amount) external returns (bool);
   176      function permit(address target, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
   177      function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
   178      function transferWithPermit(address target, address to, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external returns (bool);
   179  
   180      event Transfer(address indexed from, address indexed to, uint256 value);
   181      event Approval(address indexed owner, address indexed spender, uint256 value);
   182  }
   183  
   184  contract AnyswapV4Router {
   185      using SafeMathSushiswap for uint;
   186  
   187      address public immutable factory;
   188      address public immutable wNATIVE;
   189  
   190      modifier ensure(uint deadline) {
   191          require(deadline >= block.timestamp, 'AnyswapV3Router: EXPIRED');
   192          _;
   193      }
   194  
   195      constructor(address _factory, address _wNATIVE, address _mpc) {
   196          _newMPC = _mpc;
   197          _newMPCEffectiveTime = block.timestamp;
   198          factory = _factory;
   199          wNATIVE = _wNATIVE;
   200      }
   201  
   202      receive() external payable {
   203          assert(msg.sender == wNATIVE); // only accept Native via fallback from the wNative contract
   204      }
   205  
   206      address private _oldMPC;
   207      address private _newMPC;
   208      uint256 private _newMPCEffectiveTime;
   209  
   210  
   211      event LogChangeMPC(address indexed oldMPC, address indexed newMPC, uint indexed effectiveTime, uint chainID);
   212      event LogChangeRouter(address indexed oldRouter, address indexed newRouter, uint chainID);
   213      event LogAnySwapIn(bytes32 indexed txhash, address indexed token, address indexed to, uint amount, uint fromChainID, uint toChainID);
   214      event LogAnySwapOut(address indexed token, address indexed from, address indexed to, uint amount, uint fromChainID, uint toChainID);
   215      event LogAnySwapTradeTokensForTokens(address[] path, address indexed from, address indexed to, uint amountIn, uint amountOutMin, uint fromChainID, uint toChainID);
   216      event LogAnySwapTradeTokensForNative(address[] path, address indexed from, address indexed to, uint amountIn, uint amountOutMin, uint fromChainID, uint toChainID);
   217  
   218      modifier onlyMPC() {
   219          require(msg.sender == mpc(), "AnyswapV3Router: FORBIDDEN");
   220          _;
   221      }
   222  
   223      function mpc() public view returns (address) {
   224          if (block.timestamp >= _newMPCEffectiveTime) {
   225              return _newMPC;
   226          }
   227          return _oldMPC;
   228      }
   229  
   230      function cID() public view returns (uint id) {
   231          assembly {id := chainid()}
   232      }
   233  
   234      function changeMPC(address newMPC) public onlyMPC returns (bool) {
   235          require(newMPC != address(0), "AnyswapV3Router: address(0x0)");
   236          _oldMPC = mpc();
   237          _newMPC = newMPC;
   238          _newMPCEffectiveTime = block.timestamp + 2*24*3600;
   239          emit LogChangeMPC(_oldMPC, _newMPC, _newMPCEffectiveTime, cID());
   240          return true;
   241      }
   242  
   243      function changeVault(address token, address newVault) public onlyMPC returns (bool) {
   244          require(newVault != address(0), "AnyswapV3Router: address(0x0)");
   245          return AnyswapV1ERC20(token).changeVault(newVault);
   246      }
   247  
   248      function _anySwapOut(address from, address token, address to, uint amount, uint toChainID) internal {
   249          AnyswapV1ERC20(token).burn(from, amount);
   250          emit LogAnySwapOut(token, from, to, amount, cID(), toChainID);
   251      }
   252  
   253      // Swaps `amount` `token` from this chain to `toChainID` chain with recipient `to`
   254      function anySwapOut(address token, address to, uint amount, uint toChainID) external {
   255          _anySwapOut(msg.sender, token, to, amount, toChainID);
   256      }
   257  
   258      // Swaps `amount` `token` from this chain to `toChainID` chain with recipient `to` by minting with `underlying`
   259      function anySwapOutUnderlying(address token, address to, uint amount, uint toChainID) external {
   260          TransferHelper.safeTransferFrom(AnyswapV1ERC20(token).underlying(), msg.sender, token, amount);
   261          AnyswapV1ERC20(token).depositVault(amount, msg.sender);
   262          _anySwapOut(msg.sender, token, to, amount, toChainID);
   263      }
   264  
   265      function anySwapOutUnderlyingWithPermit(
   266          address from,
   267          address token,
   268          address to,
   269          uint amount,
   270          uint deadline,
   271          uint8 v,
   272          bytes32 r,
   273          bytes32 s,
   274          uint toChainID
   275      ) external {
   276          address _underlying = AnyswapV1ERC20(token).underlying();
   277          IERC20(_underlying).permit(from, address(this), amount, deadline, v, r, s);
   278          TransferHelper.safeTransferFrom(_underlying, from, token, amount);
   279          AnyswapV1ERC20(token).depositVault(amount, from);
   280          _anySwapOut(from, token, to, amount, toChainID);
   281      }
   282  
   283      function anySwapOutUnderlyingWithTransferPermit(
   284          address from,
   285          address token,
   286          address to,
   287          uint amount,
   288          uint deadline,
   289          uint8 v,
   290          bytes32 r,
   291          bytes32 s,
   292          uint toChainID
   293      ) external {
   294          IERC20(AnyswapV1ERC20(token).underlying()).transferWithPermit(from, token, amount, deadline, v, r, s);
   295          AnyswapV1ERC20(token).depositVault(amount, from);
   296          _anySwapOut(from, token, to, amount, toChainID);
   297      }
   298  
   299      function anySwapOut(address[] calldata tokens, address[] calldata to, uint[] calldata amounts, uint[] calldata toChainIDs) external {
   300          for (uint i = 0; i < tokens.length; i++) {
   301              _anySwapOut(msg.sender, tokens[i], to[i], amounts[i], toChainIDs[i]);
   302          }
   303      }
   304  
   305      // swaps `amount` `token` in `fromChainID` to `to` on this chainID
   306      function _anySwapIn(bytes32 txs, address token, address to, uint amount, uint fromChainID) internal {
   307          AnyswapV1ERC20(token).mint(to, amount);
   308          emit LogAnySwapIn(txs, token, to, amount, fromChainID, cID());
   309      }
   310  
   311      // swaps `amount` `token` in `fromChainID` to `to` on this chainID
   312      // triggered by `anySwapOut`
   313      function anySwapIn(bytes32 txs, address token, address to, uint amount, uint fromChainID) external onlyMPC {
   314          _anySwapIn(txs, token, to, amount, fromChainID);
   315      }
   316  
   317      // swaps `amount` `token` in `fromChainID` to `to` on this chainID with `to` receiving `underlying`
   318      function anySwapInUnderlying(bytes32 txs, address token, address to, uint amount, uint fromChainID) external onlyMPC {
   319          _anySwapIn(txs, token, to, amount, fromChainID);
   320          AnyswapV1ERC20(token).withdrawVault(to, amount, to);
   321      }
   322  
   323      // swaps `amount` `token` in `fromChainID` to `to` on this chainID with `to` receiving `underlying` if possible
   324      function anySwapInAuto(bytes32 txs, address token, address to, uint amount, uint fromChainID) external onlyMPC {
   325          _anySwapIn(txs, token, to, amount, fromChainID);
   326          AnyswapV1ERC20 _anyToken = AnyswapV1ERC20(token);
   327          address _underlying = _anyToken.underlying();
   328          if (_underlying != address(0) && IERC20(_underlying).balanceOf(token) >= amount) {
   329              _anyToken.withdrawVault(to, amount, to);
   330          }
   331      }
   332  
   333      // extracts mpc fee from bridge fees
   334      function anySwapFeeTo(address token, uint amount) external onlyMPC {
   335          address _mpc = mpc();
   336          AnyswapV1ERC20(token).mint(_mpc, amount);
   337          AnyswapV1ERC20(token).withdrawVault(_mpc, amount, _mpc);
   338      }
   339  
   340      function anySwapIn(bytes32[] calldata txs, address[] calldata tokens, address[] calldata to, uint256[] calldata amounts, uint[] calldata fromChainIDs) external onlyMPC {
   341          for (uint i = 0; i < tokens.length; i++) {
   342              _anySwapIn(txs[i], tokens[i], to[i], amounts[i], fromChainIDs[i]);
   343          }
   344      }
   345  
   346      // **** SWAP ****
   347      // requires the initial amount to have already been sent to the first pair
   348      function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {
   349          for (uint i; i < path.length - 1; i++) {
   350              (address input, address output) = (path[i], path[i + 1]);
   351              (address token0,) = SushiswapV2Library.sortTokens(input, output);
   352              uint amountOut = amounts[i + 1];
   353              (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
   354              address to = i < path.length - 2 ? SushiswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
   355              ISushiswapV2Pair(SushiswapV2Library.pairFor(factory, input, output)).swap(
   356                  amount0Out, amount1Out, to, new bytes(0)
   357              );
   358          }
   359      }
   360  
   361      // sets up a cross-chain trade from this chain to `toChainID` for `path` trades to `to`
   362      function anySwapOutExactTokensForTokens(
   363          uint amountIn,
   364          uint amountOutMin,
   365          address[] calldata path,
   366          address to,
   367          uint deadline,
   368          uint toChainID
   369      ) external virtual ensure(deadline) {
   370          AnyswapV1ERC20(path[0]).burn(msg.sender, amountIn);
   371          emit LogAnySwapTradeTokensForTokens(path, msg.sender, to, amountIn, amountOutMin, cID(), toChainID);
   372      }
   373  
   374      // sets up a cross-chain trade from this chain to `toChainID` for `path` trades to `to`
   375      function anySwapOutExactTokensForTokensUnderlying(
   376          uint amountIn,
   377          uint amountOutMin,
   378          address[] calldata path,
   379          address to,
   380          uint deadline,
   381          uint toChainID
   382      ) external virtual ensure(deadline) {
   383          TransferHelper.safeTransferFrom(AnyswapV1ERC20(path[0]).underlying(), msg.sender, path[0], amountIn);
   384          AnyswapV1ERC20(path[0]).depositVault(amountIn, msg.sender);
   385          AnyswapV1ERC20(path[0]).burn(msg.sender, amountIn);
   386          emit LogAnySwapTradeTokensForTokens(path, msg.sender, to, amountIn, amountOutMin, cID(), toChainID);
   387      }
   388  
   389      // sets up a cross-chain trade from this chain to `toChainID` for `path` trades to `to`
   390      function anySwapOutExactTokensForTokensUnderlyingWithPermit(
   391          address from,
   392          uint amountIn,
   393          uint amountOutMin,
   394          address[] calldata path,
   395          address to,
   396          uint deadline,
   397          uint8 v,
   398          bytes32 r,
   399          bytes32 s,
   400          uint toChainID
   401      ) external virtual ensure(deadline) {
   402          address _underlying = AnyswapV1ERC20(path[0]).underlying();
   403          IERC20(_underlying).permit(from, address(this), amountIn, deadline, v, r, s);
   404          TransferHelper.safeTransferFrom(_underlying, from, path[0], amountIn);
   405          AnyswapV1ERC20(path[0]).depositVault(amountIn, from);
   406          AnyswapV1ERC20(path[0]).burn(from, amountIn);
   407          {
   408          address[] memory _path = path;
   409          address _from = from;
   410          address _to = to;
   411          uint _amountIn = amountIn;
   412          uint _amountOutMin = amountOutMin;
   413          uint _cID = cID();
   414          uint _toChainID = toChainID;
   415          emit LogAnySwapTradeTokensForTokens(_path, _from, _to, _amountIn, _amountOutMin, _cID, _toChainID);
   416          }
   417      }
   418  
   419      // sets up a cross-chain trade from this chain to `toChainID` for `path` trades to `to`
   420      function anySwapOutExactTokensForTokensUnderlyingWithTransferPermit(
   421          address from,
   422          uint amountIn,
   423          uint amountOutMin,
   424          address[] calldata path,
   425          address to,
   426          uint deadline,
   427          uint8 v,
   428          bytes32 r,
   429          bytes32 s,
   430          uint toChainID
   431      ) external virtual ensure(deadline) {
   432          IERC20(AnyswapV1ERC20(path[0]).underlying()).transferWithPermit(from, path[0], amountIn, deadline, v, r, s);
   433          AnyswapV1ERC20(path[0]).depositVault(amountIn, from);
   434          AnyswapV1ERC20(path[0]).burn(from, amountIn);
   435          emit LogAnySwapTradeTokensForTokens(path, from, to, amountIn, amountOutMin, cID(), toChainID);
   436      }
   437  
   438      // Swaps `amounts[path.length-1]` `path[path.length-1]` to `to` on this chain
   439      // Triggered by `anySwapOutExactTokensForTokens`
   440      function anySwapInExactTokensForTokens(
   441          bytes32 txs,
   442          uint amountIn,
   443          uint amountOutMin,
   444          address[] calldata path,
   445          address to,
   446          uint deadline,
   447          uint fromChainID
   448      ) external onlyMPC virtual ensure(deadline) returns (uint[] memory amounts) {
   449          amounts = SushiswapV2Library.getAmountsOut(factory, amountIn, path);
   450          require(amounts[amounts.length - 1] >= amountOutMin, 'SushiswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
   451          _anySwapIn(txs, path[0], SushiswapV2Library.pairFor(factory, path[0], path[1]), amounts[0], fromChainID);
   452          _swap(amounts, path, to);
   453      }
   454  
   455      // sets up a cross-chain trade from this chain to `toChainID` for `path` trades to `to`
   456      function anySwapOutExactTokensForNative(
   457          uint amountIn,
   458          uint amountOutMin,
   459          address[] calldata path,
   460          address to,
   461          uint deadline,
   462          uint toChainID
   463      ) external virtual ensure(deadline) {
   464          AnyswapV1ERC20(path[0]).burn(msg.sender, amountIn);
   465          emit LogAnySwapTradeTokensForNative(path, msg.sender, to, amountIn, amountOutMin, cID(), toChainID);
   466      }
   467  
   468      // sets up a cross-chain trade from this chain to `toChainID` for `path` trades to `to`
   469      function anySwapOutExactTokensForNativeUnderlying(
   470          uint amountIn,
   471          uint amountOutMin,
   472          address[] calldata path,
   473          address to,
   474          uint deadline,
   475          uint toChainID
   476      ) external virtual ensure(deadline) {
   477          TransferHelper.safeTransferFrom(AnyswapV1ERC20(path[0]).underlying(), msg.sender, path[0], amountIn);
   478          AnyswapV1ERC20(path[0]).depositVault(amountIn, msg.sender);
   479          AnyswapV1ERC20(path[0]).burn(msg.sender, amountIn);
   480          emit LogAnySwapTradeTokensForNative(path, msg.sender, to, amountIn, amountOutMin, cID(), toChainID);
   481      }
   482  
   483      // sets up a cross-chain trade from this chain to `toChainID` for `path` trades to `to`
   484      function anySwapOutExactTokensForNativeUnderlyingWithPermit(
   485          address from,
   486          uint amountIn,
   487          uint amountOutMin,
   488          address[] calldata path,
   489          address to,
   490          uint deadline,
   491          uint8 v,
   492          bytes32 r,
   493          bytes32 s,
   494          uint toChainID
   495      ) external virtual ensure(deadline) {
   496          address _underlying = AnyswapV1ERC20(path[0]).underlying();
   497          IERC20(_underlying).permit(from, address(this), amountIn, deadline, v, r, s);
   498          TransferHelper.safeTransferFrom(_underlying, from, path[0], amountIn);
   499          AnyswapV1ERC20(path[0]).depositVault(amountIn, from);
   500          AnyswapV1ERC20(path[0]).burn(from, amountIn);
   501          {
   502          address[] memory _path = path;
   503          address _from = from;
   504          address _to = to;
   505          uint _amountIn = amountIn;
   506          uint _amountOutMin = amountOutMin;
   507          uint _cID = cID();
   508          uint _toChainID = toChainID;
   509          emit LogAnySwapTradeTokensForNative(_path, _from, _to, _amountIn, _amountOutMin, _cID, _toChainID);
   510          }
   511      }
   512  
   513      // sets up a cross-chain trade from this chain to `toChainID` for `path` trades to `to`
   514      function anySwapOutExactTokensForNativeUnderlyingWithTransferPermit(
   515          address from,
   516          uint amountIn,
   517          uint amountOutMin,
   518          address[] calldata path,
   519          address to,
   520          uint deadline,
   521          uint8 v,
   522          bytes32 r,
   523          bytes32 s,
   524          uint toChainID
   525      ) external virtual ensure(deadline) {
   526          IERC20(AnyswapV1ERC20(path[0]).underlying()).transferWithPermit(from, path[0], amountIn, deadline, v, r, s);
   527          AnyswapV1ERC20(path[0]).depositVault(amountIn, from);
   528          AnyswapV1ERC20(path[0]).burn(from, amountIn);
   529          emit LogAnySwapTradeTokensForNative(path, from, to, amountIn, amountOutMin, cID(), toChainID);
   530      }
   531  
   532      // Swaps `amounts[path.length-1]` `path[path.length-1]` to `to` on this chain
   533      // Triggered by `anySwapOutExactTokensForNative`
   534      function anySwapInExactTokensForNative(
   535          bytes32 txs,
   536          uint amountIn,
   537          uint amountOutMin,
   538          address[] calldata path,
   539          address to,
   540          uint deadline,
   541          uint fromChainID
   542      ) external onlyMPC virtual ensure(deadline) returns (uint[] memory amounts) {
   543          require(path[path.length - 1] == wNATIVE, 'AnyswapV3Router: INVALID_PATH');
   544          amounts = SushiswapV2Library.getAmountsOut(factory, amountIn, path);
   545          require(amounts[amounts.length - 1] >= amountOutMin, 'AnyswapV3Router: INSUFFICIENT_OUTPUT_AMOUNT');
   546          _anySwapIn(txs, path[0],  SushiswapV2Library.pairFor(factory, path[0], path[1]), amounts[0], fromChainID);
   547          _swap(amounts, path, address(this));
   548          IwNATIVE(wNATIVE).withdraw(amounts[amounts.length - 1]);
   549          TransferHelper.safeTransferNative(to, amounts[amounts.length - 1]);
   550      }
   551  
   552      // **** LIBRARY FUNCTIONS ****
   553      function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual returns (uint amountB) {
   554          return SushiswapV2Library.quote(amountA, reserveA, reserveB);
   555      }
   556  
   557      function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
   558          public
   559          pure
   560          virtual
   561          returns (uint amountOut)
   562      {
   563          return SushiswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
   564      }
   565  
   566      function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
   567          public
   568          pure
   569          virtual
   570          returns (uint amountIn)
   571      {
   572          return SushiswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
   573      }
   574  
   575      function getAmountsOut(uint amountIn, address[] memory path)
   576          public
   577          view
   578          virtual
   579          returns (uint[] memory amounts)
   580      {
   581          return SushiswapV2Library.getAmountsOut(factory, amountIn, path);
   582      }
   583  
   584      function getAmountsIn(uint amountOut, address[] memory path)
   585          public
   586          view
   587          virtual
   588          returns (uint[] memory amounts)
   589      {
   590          return SushiswapV2Library.getAmountsIn(factory, amountOut, path);
   591      }
   592  }