// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.20; import {IBroker} from "./IBroker.sol"; import {TransferHelper} from "./TransferHelper.sol"; contract MentoRouter { struct Step { address exchangeProvider; bytes32 exchangeId; address assetIn; address assetOut; } IBroker immutable broker; constructor(address _broker) { broker = IBroker(_broker); } function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, Step[] calldata path ) external returns (uint256[] memory amounts) { amounts = getAmountsOut(amountIn, path); require( amounts[amounts.length - 1] >= amountOutMin, "MentoRouter: INSUFFICIENT_OUTPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0].assetIn, msg.sender, address(this), amounts[0] ); swap(amounts, path); } function swapTokensForExactTokens( uint amountOut, uint amountInMax, Step[] calldata path ) external returns (uint[] memory amounts) { amounts = getAmountsIn(amountOut, path); require( amounts[0] <= amountInMax, "MentoRouter: EXCESSIVE_INPUT_AMOUNT" ); TransferHelper.safeTransferFrom( path[0].assetIn, msg.sender, address(this), amounts[0] ); swap(amounts, path); } function swap( uint256[] memory amounts, Step[] memory path ) internal virtual { for (uint i; i <= path.length - 1; i++) { TransferHelper.safeApprove( path[i].assetIn, address(broker), amounts[i] ); broker.swapIn( path[i].exchangeProvider, path[i].exchangeId, path[i].assetIn, path[i].assetOut, amounts[i], amounts[i + 1] ); } TransferHelper.safeTransfer( path[path.length - 1].assetOut, msg.sender, amounts[amounts.length - 1] ); } function getAmountsOut( uint256 amountIn, Step[] memory path ) internal view returns (uint256[] memory amounts) { require(path.length >= 2, "MentoRouter: INVALID_PATH"); amounts = new uint256[](path.length + 1); amounts[0] = amountIn; for (uint i; i <= path.length - 1; i++) { amounts[i + 1] = broker.getAmountOut( path[i].exchangeProvider, path[i].exchangeId, path[i].assetIn, path[i].assetOut, amounts[i] ); } return amounts; } function getAmountsIn( uint256 amountOut, Step[] memory path ) internal view returns (uint256[] memory amounts) { require(path.length >= 2, "MentoRouter: INVALID_PATH"); amounts = new uint256[](path.length + 1); amounts[amounts.length] = amountOut; for (uint i = path.length; i > 0; i--) { amounts[i - 1] = broker.getAmountIn( path[i - 1].exchangeProvider, path[i - 1].exchangeId, path[i - 1].assetIn, path[i - 1].assetOut, amounts[i] ); } return amounts; } }