diff --git a/contracts/connectors/uniswap.sol b/contracts/connectors/uniswap.sol index 8c5eacd..01d5865 100644 --- a/contracts/connectors/uniswap.sol +++ b/contracts/connectors/uniswap.sol @@ -5,6 +5,10 @@ import { TokenInterface , MemoryInterface, EventInterface} from "../common/inter import { Stores } from "../common/stores.sol"; import { DSMath } from "../common/math.sol"; +import '@uniswap/lib/contracts/libraries/Babylonian.sol'; +import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; + + interface IUniswapV2Router02 { function factory() external pure returns (address); function WETH() external pure returns (address); @@ -43,6 +47,7 @@ interface IUniswapV2Router02 { uint deadline ) external returns (uint[] memory amounts); + function getReserves(address factory, address tokenA, address tokenB) external view returns (uint reserveA, uint reserveB); 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); @@ -61,7 +66,24 @@ interface IUniswapV2Factory { function createPair(address tokenA, address tokenB) external returns (address pair); } +interface IUniswapV2Pair { + function balanceOf(address owner) external view returns (uint); + + function approve(address spender, uint value) external returns (bool); + function transfer(address to, uint value) external returns (bool); + function transferFrom(address from, address to, uint value) external returns (bool); + + function factory() external view returns (address); + function token0() external view returns (address); + function token1() external view returns (address); + function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); + function price0CumulativeLast() external view returns (uint); + function price1CumulativeLast() external view returns (uint); +} + contract UniswapHelpers is Stores, DSMath { + using SafeMath for uint256; + /** * @dev Return WETH address */ @@ -144,6 +166,19 @@ contract UniswapHelpers is Stores, DSMath { paths[0] = address(sellAddr); paths[1] = address(buyAddr); } + + + function calculateSwapInAmount(uint256 reserveIn, uint256 userIn) + internal + pure + returns (uint256) + { + return + Babylonian + .sqrt( + reserveIn.mul(userIn.mul(3988000).add(reserveIn.mul(3988009))) + ).sub(reserveIn.mul(1997)) / 1994; + } } contract LiquidityHelpers is UniswapHelpers { @@ -169,33 +204,119 @@ contract LiquidityHelpers is UniswapHelpers { function _addLiquidity( address tokenA, address tokenB, - uint _amt, - uint unitAmt, - uint slippage + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin ) internal returns (uint _amtA, uint _amtB, uint _liquidity) { IUniswapV2Router02 router = IUniswapV2Router02(getUniswapAddr()); (TokenInterface _tokenA, TokenInterface _tokenB) = changeEthAddress(tokenA, tokenB); - _amtA = _amt == uint(-1) ? getTokenBalace(tokenA) : _amt; - _amtB = convert18ToDec(_tokenB.decimals(), wmul(unitAmt, convertTo18(_tokenA.decimals(), _amtA))); + convertEthToWeth(_tokenA, amountADesired); + convertEthToWeth(_tokenB, amountBDesired); + _tokenA.approve(address(router), 0); + _tokenA.approve(address(router), amountADesired); - convertEthToWeth(_tokenA, _amtA); - convertEthToWeth(_tokenB, _amtB); - _tokenA.approve(address(router), _amtA); - _tokenB.approve(address(router), _amtB); + _tokenB.approve(address(router), 0); + _tokenB.approve(address(router), amountBDesired); - uint minAmtA = getMinAmount(_tokenA, _amtA, slippage); - uint minAmtB = getMinAmount(_tokenB, _amtB, slippage); (_amtA, _amtB, _liquidity) = router.addLiquidity( address(_tokenA), address(_tokenB), - _amtA, - _amtB, - minAmtA, - minAmtB, + amountADesired, + amountBDesired, + amountAMin, + amountBMin, address(this), now + 1 ); + + if (_amtA < amountADesired) { + convertWethToEth(_tokenA, _tokenA.balanceOf(address(this))); + } + + if (_amtB < amountBDesired) { + convertWethToEth(_tokenB, _tokenB.balanceOf(address(this))); + } + } + + function _addLiquiditySingle( + address tokenA, + address tokenB, + uint amountA, + uint minUniAmount + ) internal returns (uint _amtA, uint _amtB, uint _liquidity) { + IUniswapV2Router02 router = IUniswapV2Router02(getUniswapAddr()); + (TokenInterface _tokenA, TokenInterface _tokenB) = changeEthAddress(tokenA, tokenB); + + uint256 _amountA; + + if (amountA == uint(-1)) { + _amountA = tokenA == getEthAddr() ? address(this).balance : _tokenA.balanceOf(address(this)); + } + + convertEthToWeth(_tokenA, _amountA); + + + uint256 _amountB; + + (_amountA, _amountB)= _swapSingleToken(router, _tokenA, _tokenB, _amountA); + + _tokenA.approve(address(router), 0); + _tokenA.approve(address(router), _amountA); + + _tokenB.approve(address(router), 0); + _tokenB.approve(address(router), _amountB); + + (_amtA, _amtB, _liquidity) = router.addLiquidity( + address(_tokenA), + address(_tokenB), + _amountA, + _amountB, + 1, // TODO @thrilok209: check this + 1, // TODO @thrilok209: check this + address(this), + now + 1 + ); + + require(_liquidity >= minUniAmount, "too much slippage"); + + if (_amountA > _amtA) { + convertWethToEth(_tokenA, _tokenA.balanceOf(address(this))); + } + + if (_amountB > _amtB) { + convertWethToEth(_tokenB, _tokenB.balanceOf(address(this))); + } + } + + function _swapSingleToken( + IUniswapV2Router02 router, + TokenInterface tokenA, + TokenInterface tokenB, + uint _amountA + ) internal returns(uint256 amountA, uint256 amountB){ + IUniswapV2Factory factory = IUniswapV2Factory(router.factory()); + IUniswapV2Pair lpToken = IUniswapV2Pair(factory.getPair(address(tokenA), address(tokenB))); + require(address(lpToken) != address(0), "No-exchange-address"); + + (uint256 reserveA, uint256 reserveB, ) = lpToken.getReserves(); + uint256 reserveIn = lpToken.token0() == address(tokenA) ? reserveA : reserveB; + uint256 swapAmtA = calculateSwapInAmount(reserveIn, _amountA); + + address[] memory paths = getPaths(address(tokenA), address(tokenB)); + + tokenA.approve(address(router), swapAmtA); + + amountB = router.swapTokensForExactTokens( + 1, // TODO @thrilok209: check this + swapAmtA, + paths, + address(this), + now + 1 + )[0]; + + amountA = sub(_amountA, swapAmtA); } function _removeLiquidity( @@ -253,7 +374,8 @@ contract UniswapLiquidity is LiquidityHelpers { uint amtA, uint amtB, uint uniAmount, - uint getId, + uint getIdA, + uint getIdB, uint setId ); @@ -267,15 +389,75 @@ contract UniswapLiquidity is LiquidityHelpers { uint[] setId ); - function emitDeposit( + + /** + * @dev Deposit Liquidity. + * @param tokenA tokenA address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB tokenB address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amountADesired tokenA amount. + * @param amountBDesired unit amount of amtB/amtA with slippage. + * @param amountAMin slippage amount. + * @param amountBMin slippage amount. + * @param getIdA Get token amount at this ID from `InstaMemory` Contract. + * @param getIdB Get tokens amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function deposit( address tokenA, address tokenB, - uint _amtA, - uint _amtB, - uint _uniAmt, + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin, + uint getIdA, + uint getIdB, + uint setId + ) external payable { + uint _amtADesired = getUint(getIdA, amountADesired); + uint _amtBDesired = getUint(getIdB, amountBDesired); + + (uint _amtA, uint _amtB, uint _uniAmt) = _addLiquidity( + tokenA, + tokenB, + _amtADesired, + _amtBDesired, + amountAMin, + amountBMin + ); + setUint(setId, _uniAmt); + + emit LogDepositLiquidity( + tokenA, + tokenB, + _amtA, + _amtB, + _uniAmt, + getIdA, + getIdB, + setId + ); + + } + + + function singleDeposit( + address tokenA, + address tokenB, + uint amountA, + uint minUniAmount, uint getId, uint setId - ) internal { + ) external payable { + uint _amt = getUint(getId, amountA); + + (uint _amtA, uint _amtB, uint _uniAmt) = _addLiquiditySingle( + tokenA, + tokenB, + _amt, + minUniAmount + ); + setUint(setId, _uniAmt); + emit LogDepositLiquidity( tokenA, tokenB, @@ -283,84 +465,12 @@ contract UniswapLiquidity is LiquidityHelpers { _amtB, _uniAmt, getId, + 0, setId ); - - bytes32 _eventCode = keccak256("LogDepositLiquidity(address,address,uint256,uint256,uint256,uint256,uint256)"); - bytes memory _eventParam = abi.encode( - tokenA, - tokenB, - _amtA, - _amtB, - _uniAmt, - getId, - setId - ); - emitEvent(_eventCode, _eventParam); } - function emitWithdraw( - address tokenA, - address tokenB, - uint _amtA, - uint _amtB, - uint _uniAmt, - uint getId, - uint[] memory setIds - ) internal { - emit LogWithdrawLiquidity( - tokenA, - tokenB, - _amtA, - _amtB, - _uniAmt, - getId, - setIds - ); - bytes32 _eventCode = keccak256("LogWithdrawLiquidity(address,address,uint256,uint256,uint256,uint256,uint256[])"); - bytes memory _eventParam = abi.encode( - tokenA, - tokenB, - _amtA, - _amtB, - _uniAmt, - getId, - setIds - ); - emitEvent(_eventCode, _eventParam); - } - - /** - * @dev Deposit Liquidity. - * @param tokenA tokenA address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) - * @param tokenB tokenB address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) - * @param amtA tokenA amount. - * @param unitAmt unit amount of amtB/amtA with slippage. - * @param slippage slippage amount. - * @param getId Get token amount at this ID from `InstaMemory` Contract. - * @param setId Set token amount at this ID in `InstaMemory` Contract. - */ - function deposit( - address tokenA, - address tokenB, - uint amtA, - uint unitAmt, - uint slippage, - uint getId, - uint setId - ) external payable { - uint _amt = getUint(getId, amtA); - - (uint _amtA, uint _amtB, uint _uniAmt) = _addLiquidity( - tokenA, - tokenB, - _amt, - unitAmt, - slippage - ); - setUint(setId, _uniAmt); - emitDeposit(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setId); - } + /** * @dev Withdraw Liquidity. @@ -393,7 +503,16 @@ contract UniswapLiquidity is LiquidityHelpers { setUint(setIds[0], _amtA); setUint(setIds[1], _amtB); - emitWithdraw(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setIds); + + emit LogWithdrawLiquidity( + tokenA, + tokenB, + _amtA, + _amtB, + _uniAmt, + getId, + setIds + ); } } @@ -527,5 +646,5 @@ contract UniswapResolver is UniswapLiquidity { contract ConnectUniswapV2 is UniswapResolver { - string public name = "UniswapV2-v1"; + string public name = "UniswapV2-v1.1"; } \ No newline at end of file