From 21ec4b2ff21e2607ac9e13c14f29f074226080cd Mon Sep 17 00:00:00 2001 From: midnight-commit <43814957+midnight-commit@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:44:43 +0200 Subject: [PATCH] upgrade SimpleRouter --- contracts/interfaces/ISimpleRouter.sol | 11 ++- contracts/router/SimpleRouter.sol | 94 +++++++++++++++++++++----- 2 files changed, 88 insertions(+), 17 deletions(-) diff --git a/contracts/interfaces/ISimpleRouter.sol b/contracts/interfaces/ISimpleRouter.sol index 2e451b7..186c189 100644 --- a/contracts/interfaces/ISimpleRouter.sol +++ b/contracts/interfaces/ISimpleRouter.sol @@ -7,6 +7,11 @@ interface ISimpleRouter { error UnsupportedSwap(address _tokenIn, address _tokenOut); error SlippageExceeded(); error InvalidConfiguration(); + error FeeExceedsMaximum(uint256 _feeBips, uint256 _maxFeeBips); + error InvalidFeeCollector(address _feeCollector); + + event UpdateFeeBips(uint256 _oldFeeBips, uint256 _newFeeBips); + event UpdateFeeCollector(address _oldFeeCollector, address _newFeeCollector); struct SwapConfig { bool useYakSwapRouter; @@ -22,7 +27,11 @@ interface ISimpleRouter { function query(uint256 _amountIn, address _tokenIn, address _tokenOut) external view - returns (FormattedOffer memory trade); + returns (FormattedOffer memory offer); function swap(FormattedOffer memory _trade) external returns (uint256 amountOut); + + function swap(uint256 _amountIn, uint256 _amountOutMin, address _tokenIn, address _tokenOut) + external + returns (uint256 amountOut); } diff --git a/contracts/router/SimpleRouter.sol b/contracts/router/SimpleRouter.sol index d3c06ba..cf059a3 100644 --- a/contracts/router/SimpleRouter.sol +++ b/contracts/router/SimpleRouter.sol @@ -1,21 +1,53 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.13; -import "./../interfaces/ISimpleRouter.sol"; -import "./interfaces/IYakRouter.sol"; -import "./interfaces/IAdapter.sol"; -import "./../interfaces/IERC20.sol"; -import "./../lib/Ownable.sol"; +import {ISimpleRouter} from "./../interfaces/ISimpleRouter.sol"; +import {IYakRouter, FormattedOffer} from "./interfaces/IYakRouter.sol"; +import {IAdapter} from "./interfaces/IAdapter.sol"; +import {IERC20} from "./../interfaces/IERC20.sol"; +import {Ownable} from "./../lib/Ownable.sol"; contract SimpleRouter is ISimpleRouter, Ownable { bool public yakSwapFallback; uint256 public maxStepsFallback; IYakRouter public yakRouter; + address public feeCollector; + uint256 public feeBips; + uint256 internal constant BIPS_DIVISOR = 10000; + uint256 public constant MAX_FEE_BIPS = 1000; mapping(address => mapping(address => SwapConfig)) public swapConfigurations; - constructor(bool _yakSwapFallback, uint256 _maxStepsFallback, address _yakRouter) { + constructor( + bool _yakSwapFallback, + uint256 _maxStepsFallback, + address _yakRouter, + uint256 _feeBips, + address _feeCollector + ) { configureYakSwapDefaults(_yakSwapFallback, _maxStepsFallback, _yakRouter); + updateFeeBips(_feeBips); + updateFeeCollector(_feeCollector); + } + + function updateFeeBips(uint256 _feeBips) public onlyOwner { + if (_feeBips > MAX_FEE_BIPS) { + revert FeeExceedsMaximum(_feeBips, MAX_FEE_BIPS); + } + if (_feeBips != feeBips) { + emit UpdateFeeBips(feeBips, _feeBips); + feeBips = _feeBips; + } + } + + function updateFeeCollector(address _feeCollector) public onlyOwner { + if (_feeCollector == address(0)) { + revert InvalidFeeCollector(_feeCollector); + } + if (_feeCollector != feeCollector) { + emit UpdateFeeCollector(feeCollector, _feeCollector); + feeCollector = _feeCollector; + } } function updateSwapConfiguration(SwapConfig memory _swapConfig) external onlyOwner { @@ -40,28 +72,40 @@ contract SimpleRouter is ISimpleRouter, Ownable { } function query(uint256 _amountIn, address _tokenIn, address _tokenOut) - public + external view override returns (FormattedOffer memory offer) { SwapConfig storage swapConfig = swapConfigurations[_tokenIn][_tokenOut]; - bool routeConfigured = swapConfig.path.adapters.length > 0; + return query(_amountIn, _tokenIn, _tokenOut, swapConfig); + } - if (!routeConfigured && !swapConfig.useYakSwapRouter && !yakSwapFallback) { + function query(uint256 _amountIn, address _tokenIn, address _tokenOut, SwapConfig memory _swapConfig) + public + view + returns (FormattedOffer memory offer) + { + bool routeConfigured = _swapConfig.path.adapters.length > 0; + + if (!routeConfigured && !_swapConfig.useYakSwapRouter && !yakSwapFallback) { return zeroOffer(_tokenIn, _tokenOut); } + uint256 amountIn = _amountIn; + _amountIn = _amountIn * (BIPS_DIVISOR - feeBips) / BIPS_DIVISOR; + if (routeConfigured) { - offer = queryPredefinedRoute(_amountIn, swapConfig.path.adapters, swapConfig.path.tokens); + offer = queryPredefinedRoute(_amountIn, _swapConfig.path.adapters, _swapConfig.path.tokens); } else { offer = queryYakSwap( _amountIn, _tokenIn, _tokenOut, - swapConfig.yakSwapMaxSteps > 0 ? swapConfig.yakSwapMaxSteps : maxStepsFallback + _swapConfig.yakSwapMaxSteps > 0 ? _swapConfig.yakSwapMaxSteps : maxStepsFallback ); } + offer.amounts[0] = amountIn; } function queryPredefinedRoute(uint256 _amountIn, address[] memory _adapters, address[] memory _tokens) @@ -69,7 +113,7 @@ contract SimpleRouter is ISimpleRouter, Ownable { view returns (FormattedOffer memory offer) { - uint256[] memory amounts = new uint[](_tokens.length); + uint256[] memory amounts = new uint256[](_tokens.length); amounts[0] = _amountIn; for (uint256 i; i < _adapters.length; i++) { amounts[i + 1] = IAdapter(_adapters[i]).query(amounts[i], _tokens[i], _tokens[i + 1]); @@ -107,21 +151,39 @@ contract SimpleRouter is ISimpleRouter, Ownable { return _swap(tokenIn, _offer); } - function swap(uint _amountIn, uint _amountOutMin, address _tokenIn, address _tokenOut) external returns (uint amountOut) { - FormattedOffer memory offer = query(_amountIn, _tokenIn, _tokenOut); + function swap(uint256 _amountIn, uint256 _amountOutMin, address _tokenIn, address _tokenOut) + external + returns (uint256 amountOut) + { + SwapConfig memory swapConfig = swapConfigurations[_tokenIn][_tokenOut]; + return swap(_amountIn, _amountOutMin, _tokenIn, _tokenOut, swapConfig); + } + + function swap( + uint256 _amountIn, + uint256 _amountOutMin, + address _tokenIn, + address _tokenOut, + SwapConfig memory _swapConfig + ) public returns (uint256 amountOut) { + uint256 fee = _amountIn * feeBips / BIPS_DIVISOR; + IERC20(_tokenIn).transferFrom(msg.sender, feeCollector, fee); + + _amountIn -= fee; + FormattedOffer memory offer = query(_amountIn, _tokenIn, _tokenOut, _swapConfig); if (offer.adapters.length == 0) { revert UnsupportedSwap(_tokenIn, _tokenOut); } - if (offer.amounts[offer.amounts.length -1] < _amountOutMin) { + if (offer.amounts[offer.amounts.length - 1] < _amountOutMin) { revert SlippageExceeded(); } return _swap(_tokenIn, offer); } - function _swap(address _tokenIn, FormattedOffer memory _offer) internal returns(uint amountOut) { + function _swap(address _tokenIn, FormattedOffer memory _offer) internal returns (uint256 amountOut) { IERC20(_tokenIn).transferFrom(msg.sender, _offer.adapters[0], _offer.amounts[0]); for (uint256 i; i < _offer.adapters.length; i++) {