pragma solidity >=0.6.12; import { IERC20 } from "Interfaces.sol"; import { SafeMath } from "Libraries.sol"; interface IKSP { function exchangeKlayPos(address token, uint256 amount, address[] memory path) external payable; function exchangeKctPos(address tokenA, uint256 amountA, address tokenB, uint256 amountB, address[] memory path) external; function exchangeKlayNeg(address token, uint256 amount, address[] memory path) external payable; function exchangeKctNeg(address tokenA, uint256 amountA, address tokenB, uint256 amountB, address[] memory path) external; function tokenToPool(address tokenA, address tokenB) external view returns (address); function poolExist(address pool) external view returns (bool); } interface IKSLP { function tokenA() external view returns (address); function tokenB() external view returns (address); function claimReward() external; function estimatePos(address token, uint256 amount) external view returns (uint256); function estimateNeg(address token, uint256 amount) external view returns (uint256); function addKlayLiquidity(uint256 amount) external payable; function addKctLiquidity(uint256 amountA, uint256 amountB) external; function removeLiquidity(uint256 amount) external; function getCurrentPool() external view returns (uint256, uint256); function addKctLiquidityWithLimit(uint256 amountA, uint256 amountB, uint256 minAmountA, uint256 minAmountB) external; } interface IUniswapV2Router01 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB, uint liquidity); function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external payable returns (uint amountToken, uint amountETH, uint liquidity); function removeLiquidity( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB); function removeLiquidityETH( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountToken, uint amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountA, uint amountB); function removeLiquidityETHWithPermit( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountToken, uint amountETH); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); } interface IUniswapV2Factory { event PairCreated(address indexed token0, address indexed token1, address pair, uint); function feeTo() external view returns (address); function feeRate() external view returns (uint); function feeToSetter() external view returns (address); function getPair(address tokenA, address tokenB) external view returns (address pair); function allPairs(uint) external view returns (address pair); function allPairsLength() external view returns (uint); function createPair(address tokenA, address tokenB) external returns (address pair); function setFeeTo(address) external; function setFeeRate(uint) external; function setFeeToSetter(address) external; } interface IWETH { function deposit() external payable; function transfer(address to, uint value) external returns (bool); function withdraw(uint) external; } interface iReadArbTrader { function getStartPath(uint256 _CoinNo) external view returns (address[] memory _Startpath); function getEndPath(uint256 _CoinNo) external view returns (address[] memory _Endpath); function getRouterAdd(uint256 _CoinNo) external view returns (address _Routerpath); } contract ReadArbTrader { address payable Na_owner; address[] Routerpath; address[][] Startpath; address[][] Endpath; address KalySwapRouter; using SafeMath for uint256; constructor() public { Na_owner = msg.sender; Startpath = new address[][](200); Endpath = new address[][](200); } function setKlaySwapRouter(address _Routerpath) external { KalySwapRouter = _Routerpath; } function setRouter(address[] memory _Routerpath) external { Routerpath = new address[](_Routerpath.length); for(uint256 i = 0; i < _Routerpath.length; i++) { Routerpath[i] = _Routerpath[i]; } } function setPair(uint256 _CoinNo, address[] memory _Startpath, address[] memory _Endpath) external { Startpath[_CoinNo] = new address[](_Startpath.length); Endpath[_CoinNo] = new address[](_Endpath.length); for(uint256 i = 0; i < _Startpath.length; i++) { Startpath[_CoinNo][i] = _Startpath[i]; } for(uint256 i = 0; i < _Endpath.length; i++) { Endpath[_CoinNo][i] = _Endpath[i]; } } function WithdrawToken(address _token) external { IERC20 token = IERC20(_token); token.transfer(Na_owner, token.balanceOf(address(this))); } function GetAmountMax(uint256 _CoinNo, uint256 _AmountIn, uint256 StartIndex, uint256 EndIndex) internal view virtual returns (uint256 amounts) { uint256 amountToken1 = 0; uint256 amountToken2 = 0; uint256 amountToken3 = 0; uint256 amountToken4 = 0; for(uint256 i = 1; i < 5; i++) { amountToken1 = _AmountIn.div(i); amountToken2 = GetAmountsEnd(_CoinNo, EndIndex, GetAmountsStart(_CoinNo, StartIndex, amountToken1)); if (amountToken2 > amountToken1) { if ((amountToken2 - amountToken1) > amountToken3) { amountToken3 = (amountToken2 - amountToken1); amountToken4 = amountToken1; } } } amounts = amountToken4; } function GetAmountsStart(uint256 _CoinNo, uint256 StartIndex, uint256 _AmountIn) public view virtual returns (uint256 amounts) { if (KalySwapRouter != Routerpath[StartIndex]) { try IUniswapV2Router01(Routerpath[StartIndex]).getAmountsOut(_AmountIn, Startpath[_CoinNo]) returns (uint256[] memory amountsS) { amounts = amountsS[amountsS.length-1]; } catch { } } else { if (Startpath[_CoinNo].length == 2) { try IKSLP(getKlaySwapParAdd(StartIndex, Startpath[_CoinNo][0], Startpath[_CoinNo][1])).estimatePos(Startpath[_CoinNo][0], _AmountIn) returns (uint256 amountsS) { amounts = amountsS; } catch { } } else { uint256 amountsS = 0; try IKSLP(getKlaySwapParAdd(StartIndex, Startpath[_CoinNo][0], Startpath[_CoinNo][1])).estimatePos(Startpath[_CoinNo][0], _AmountIn) returns (uint256 amounts1) { amountsS = amounts1; } catch { } if (amountsS != 0) { try IKSLP(getKlaySwapParAdd(StartIndex, Startpath[_CoinNo][1], Startpath[_CoinNo][2])).estimatePos(Startpath[_CoinNo][1], amountsS) returns (uint256 amounts1) { amounts = amounts1; } catch { } } } } } function GetAmountsEnd(uint256 _CoinNo, uint256 EndIndex, uint256 _AmountIn) public view virtual returns (uint256 amounts) { if (KalySwapRouter != Routerpath[EndIndex]) { try IUniswapV2Router01(Routerpath[EndIndex]).getAmountsOut(_AmountIn, Endpath[_CoinNo]) returns (uint256[] memory amountsS) { amounts = amountsS[amountsS.length-1]; } catch { } } else { if (Endpath[_CoinNo].length == 2) { try IKSLP(getKlaySwapParAdd(EndIndex, Endpath[_CoinNo][0], Endpath[_CoinNo][1])).estimatePos(Endpath[_CoinNo][0], _AmountIn) returns (uint256 amountsS) { amounts = amountsS; } catch { } } else { uint256 amountsS = 0; try IKSLP(getKlaySwapParAdd(EndIndex, Endpath[_CoinNo][0], Endpath[_CoinNo][1])).estimatePos(Endpath[_CoinNo][0], _AmountIn) returns (uint256 amounts1) { amountsS = amounts1; } catch { } try IKSLP(getKlaySwapParAdd(EndIndex, Endpath[_CoinNo][1], Endpath[_CoinNo][2])).estimatePos(Endpath[_CoinNo][1], amountsS) returns (uint256 amounts1) { amounts = amounts1; } catch { } } } } function GetSwapAmounts(uint256 _CoinNo, uint256 StartIndex, uint256 EndIndex, uint256 _AmountIn, uint256 _AmountOut) public view virtual returns (uint256 amounts) { uint256 amountToken = 0; uint256 amountToken2 = 0; uint256[] memory amountsToken = new uint256[](4); if (KalySwapRouter != Routerpath[StartIndex]) { amountsToken[0] = IERC20(Startpath[_CoinNo][0]).balanceOf(getUniParAdd(StartIndex, Startpath[_CoinNo][0], Startpath[_CoinNo][1])); } else { amountsToken[0] = IERC20(Startpath[_CoinNo][0]).balanceOf(getKlaySwapParAdd(StartIndex, Startpath[_CoinNo][0], Startpath[_CoinNo][1])); } if (Startpath[_CoinNo].length > 2) { if (KalySwapRouter != Routerpath[StartIndex]) { amountToken2 = getUniParAmount(StartIndex, _AmountIn, Startpath[_CoinNo][0], Startpath[_CoinNo][1]); amountsToken[2] = IERC20(Startpath[_CoinNo][1]).balanceOf(getUniParAdd(StartIndex, Startpath[_CoinNo][1], Startpath[_CoinNo][2])) / amountToken2; } else { amountToken2 = getKlaySwapParAmount(StartIndex, _AmountIn, Startpath[_CoinNo][0], Startpath[_CoinNo][1]); amountsToken[2] = IERC20(Startpath[_CoinNo][1]).balanceOf(getKlaySwapParAdd(StartIndex, Startpath[_CoinNo][1], Startpath[_CoinNo][2])) / amountToken2; } amountsToken[2] = amountsToken[2] * _AmountIn; } if (Endpath[_CoinNo].length > 2) { if (KalySwapRouter != Routerpath[EndIndex]) { amountsToken[1] = IERC20(Startpath[_CoinNo][0]).balanceOf(getUniParAdd(EndIndex, Endpath[_CoinNo][1], Endpath[_CoinNo][2])); } else { amountsToken[1] = IERC20(Startpath[_CoinNo][0]).balanceOf(getKlaySwapParAdd(EndIndex, Endpath[_CoinNo][1], Endpath[_CoinNo][2])); } if (KalySwapRouter != Routerpath[EndIndex]) { amountToken2 = getUniParAmount(EndIndex, _AmountIn, Endpath[_CoinNo][2], Endpath[_CoinNo][1]); amountsToken[3] = IERC20(Endpath[_CoinNo][1]).balanceOf(getUniParAdd(EndIndex, Endpath[_CoinNo][0], Endpath[_CoinNo][1])) / amountToken2; } else { amountToken2 = getKlaySwapParAmount(EndIndex, _AmountIn, Endpath[_CoinNo][2], Endpath[_CoinNo][1]); amountsToken[3] = IERC20(Endpath[_CoinNo][1]).balanceOf(getKlaySwapParAdd(EndIndex, Endpath[_CoinNo][0], Endpath[_CoinNo][1])) / amountToken2; } amountsToken[3] = amountsToken[3] * _AmountIn; } else { if (KalySwapRouter != Routerpath[EndIndex]) { amountsToken[1] = IERC20(Startpath[_CoinNo][0]).balanceOf(getUniParAdd(EndIndex, Endpath[_CoinNo][0], Endpath[_CoinNo][1])); } else { amountsToken[1] = IERC20(Startpath[_CoinNo][0]).balanceOf(getKlaySwapParAdd(EndIndex, Endpath[_CoinNo][0], Endpath[_CoinNo][1])); } } amountToken = ((((_AmountOut - _AmountIn) * _AmountIn) / _AmountOut) * amountsToken[0]) / _AmountIn; for(uint256 i = 1; i < amountsToken.length; i++) { if (amountsToken[i] != 0) { if (amountToken > (((((_AmountOut - _AmountIn) * _AmountIn) / _AmountOut) * amountsToken[i]) / _AmountIn)) { amountToken = ((((_AmountOut - _AmountIn) * _AmountIn) / _AmountOut) * amountsToken[i]) / _AmountIn; } } } amountToken = amountToken * 2; amounts = GetAmountMax(_CoinNo, amountToken, StartIndex, EndIndex); } function GetAmounts(uint256 _CoinNo, uint _AmountIn) public view virtual returns (uint256 amounts) { uint256 amounts0 = 0; uint256 amounts1 = 0; for(uint256 i = 0; i < Routerpath.length; i++) { uint256 amountsS = GetAmountsStart(_CoinNo, i, _AmountIn); if (amounts0 <= amountsS) { amounts0 = amountsS; } } for(uint256 i = 0; i < Routerpath.length; i++) { uint256 amountsS = GetAmountsEnd(_CoinNo, i, amounts0) ; if (amounts1 <= amountsS) { amounts1 = amountsS; } } amounts = amounts1; } function getAmountsPer(uint256 _CoinNo, uint _AmountIn) public view virtual returns (uint256 amounts, uint256 StartIndex, uint256 EndIndex ) { uint256 amounts0 = 0; uint256 amounts1 = 0; for(uint256 i = 0; i < Routerpath.length; i++) { uint256 amountsS = GetAmountsStart(_CoinNo, i, _AmountIn); if (amounts0 <= amountsS) { amounts0 = amountsS; StartIndex = i; } } for(uint256 i = 0; i < Routerpath.length; i++) { uint256 amountsS = GetAmountsEnd(_CoinNo, i, amounts0) ; if (amounts1 <= amountsS) { amounts1 = amountsS; EndIndex = i; } } amounts = amounts1; } function getUniParAmount(uint256 _CoinNo, uint _AmountIn, address _asset0, address _asset1) public view virtual returns (uint256 _Amount) { address[] memory amountspath = new address[](2); uint256[] memory amountsToken = new uint256[](2); amountspath[0] = _asset0; amountspath[1] = _asset1; amountsToken = IUniswapV2Router01(Routerpath[_CoinNo]).getAmountsOut(_AmountIn, amountspath); _Amount = amountsToken[amountsToken.length-1]; } function getKlaySwapParAmount(uint256 _CoinNo, uint _AmountIn, address _asset0, address _asset1) public view virtual returns (uint256 _Amount) { _Amount = IKSLP(getKlaySwapParAdd(_CoinNo, _asset0, _asset1)).estimatePos(_asset0, _AmountIn); } function getUniParAdd(uint256 _CoinNo, address _asset0, address _asset1) public view virtual returns (address factoryAdd) { IUniswapV2Router01 routerS = IUniswapV2Router01(Routerpath[_CoinNo]); IUniswapV2Factory factoryS = IUniswapV2Factory(routerS.factory()); factoryAdd = factoryS.getPair(_asset0, _asset1); } function getKlaySwapParAdd(uint256 _CoinNo, address _asset0, address _asset1) public view virtual returns (address factoryAdd) { IKSP routerS = IKSP(Routerpath[_CoinNo]); factoryAdd = routerS.tokenToPool(_asset0, _asset1); } function getStartPath(uint256 _CoinNo) public view virtual returns (address[] memory _Startpath) { _Startpath = Startpath[_CoinNo]; } function getEndPath(uint256 _CoinNo) public view virtual returns (address[] memory _Endpath) { _Endpath = Endpath[_CoinNo]; } function getStartAdd(uint256 _CoinNo, uint256 _PathIdx) public view virtual returns (address _Startpath) { _Startpath = Startpath[_CoinNo][_PathIdx]; } function getEndAdd(uint256 _CoinNo, uint256 _PathIdx) public view virtual returns (address _Endpath) { _Endpath = Endpath[_CoinNo][_PathIdx]; } function getRouterAdd(uint256 _CoinNo) public view virtual returns (address _Routerpath) { _Routerpath = Routerpath[_CoinNo]; } }