From 2d6ed497f1128a0d1c2137277feef6c28b3f2152 Mon Sep 17 00:00:00 2001 From: Mubaris NK Date: Sat, 13 Feb 2021 18:19:31 +0530 Subject: [PATCH] Add more connectors --- contracts/connectors/oasis/events.sol | 21 +++ contracts/connectors/oasis/interface.sol | 23 +++ contracts/connectors/oasis/main.sol | 122 ++++++++++++++ contracts/connectors/uniswap/events.sol | 41 +++++ contracts/connectors/uniswap/helpers.sol | 146 ++++++++++++++++ contracts/connectors/uniswap/interface.sol | 57 +++++++ contracts/connectors/uniswap/main.sol | 183 +++++++++++++++++++++ 7 files changed, 593 insertions(+) create mode 100644 contracts/connectors/oasis/events.sol create mode 100644 contracts/connectors/oasis/interface.sol create mode 100644 contracts/connectors/oasis/main.sol create mode 100644 contracts/connectors/uniswap/events.sol create mode 100644 contracts/connectors/uniswap/helpers.sol create mode 100644 contracts/connectors/uniswap/interface.sol create mode 100644 contracts/connectors/uniswap/main.sol diff --git a/contracts/connectors/oasis/events.sol b/contracts/connectors/oasis/events.sol new file mode 100644 index 00000000..8c9d8442 --- /dev/null +++ b/contracts/connectors/oasis/events.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogBuy( + address indexed buyToken, + address indexed sellToken, + uint256 buyAmt, + uint256 sellAmt, + uint256 getId, + uint256 setId + ); + + event LogSell( + address indexed buyToken, + address indexed sellToken, + uint256 buyAmt, + uint256 sellAmt, + uint256 getId, + uint256 setId + ); +} \ No newline at end of file diff --git a/contracts/connectors/oasis/interface.sol b/contracts/connectors/oasis/interface.sol new file mode 100644 index 00000000..e815f53e --- /dev/null +++ b/contracts/connectors/oasis/interface.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.7.0; + +import { TokenInterface } from "../../common/interfaces.sol"; + +interface OasisInterface { + function getMinSell(TokenInterface pay_gem) external view returns (uint); + function getBuyAmount(address dest, address src, uint srcAmt) external view returns(uint); + function getPayAmount(address src, address dest, uint destAmt) external view returns (uint); + function sellAllAmount( + address src, + uint srcAmt, + address dest, + uint minDest + ) external returns (uint destAmt); + function buyAllAmount( + address dest, + uint destAmt, + address src, + uint maxSrc + ) external returns (uint srcAmt); + + function getBestOffer(TokenInterface sell_gem, TokenInterface buy_gem) external view returns(uint); +} \ No newline at end of file diff --git a/contracts/connectors/oasis/main.sol b/contracts/connectors/oasis/main.sol new file mode 100644 index 00000000..26b3f9f4 --- /dev/null +++ b/contracts/connectors/oasis/main.sol @@ -0,0 +1,122 @@ +pragma solidity ^0.7.0; + +import { TokenInterface } from "../../common/interfaces.sol"; +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; +import { OasisInterface } from "./interface.sol"; +import { Events } from "./events.sol"; + +contract OasisResolver is DSMath, Basic, Events { + /** + * @dev Oasis Interface + */ + OasisInterface internal constant oasis = OasisInterface(0x794e6e91555438aFc3ccF1c5076A74F42133d08D); + + /** + * @dev Buy ETH/ERC20_Token. + * @param buyAddr buying token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr selling token amount.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param buyAmt buying token amount. + * @param unitAmt unit amount of sellAmt/buyAmt with slippage. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function buy( + address buyAddr, + address sellAddr, + uint buyAmt, + uint unitAmt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _buyAmt = getUint(getId, buyAmt); + (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeEthAddress(buyAddr, sellAddr); + + uint _slippageAmt = convert18ToDec(_sellAddr.decimals(), wmul(unitAmt, _buyAmt)); + + require(oasis.getBestOffer(_sellAddr, _buyAddr) != 0, "no-offer"); + require(oasis.getMinSell(_sellAddr) <= _slippageAmt, "less-than-min-pay-amt"); + + uint _expectedAmt = oasis.getPayAmount(address(_sellAddr), address(_buyAddr), _buyAmt); + require(_slippageAmt >= _expectedAmt, "Too much slippage"); + + bool isEth = address(_sellAddr) == wethAddr; + + convertEthToWeth(isEth, _sellAddr, _expectedAmt); + _sellAddr.approve(address(oasis), _expectedAmt); + + uint _sellAmt = oasis.buyAllAmount( + address(_buyAddr), + _buyAmt, + address(_sellAddr), + _slippageAmt + ); + + isEth = address(_buyAddr) == wethAddr; + + convertWethToEth(isEth, _buyAddr, _buyAmt); + + setUint(setId, _sellAmt); + + _eventName = "LogBuy(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); + } + + /** + * @dev Sell ETH/ERC20_Token. + * @param buyAddr buying token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr selling token amount.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAmt selling token amount. + * @param unitAmt unit amount of buyAmt/sellAmt with slippage. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function sell( + address buyAddr, + address sellAddr, + uint sellAmt, + uint unitAmt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _sellAmt = getUint(getId, sellAmt); + (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeEthAddress(buyAddr, sellAddr); + + if (_sellAmt == uint(-1)) { + _sellAmt = sellAddr == ethAddr ? address(this).balance : _buyAddr.balanceOf(address(this)); + } + + uint _slippageAmt = convert18ToDec(_buyAddr.decimals(), wmul(unitAmt, _sellAmt)); + + require(oasis.getBestOffer(_sellAddr, _buyAddr) != 0, "no-offer"); + require(oasis.getMinSell(_sellAddr) <= _sellAmt, "less-than-min-pay-amt"); + + uint _expectedAmt = oasis.getBuyAmount(address(_buyAddr), address(_sellAddr), _sellAmt); + require(_slippageAmt <= _expectedAmt, "Too much slippage"); + + bool isEth = address(_sellAddr) == wethAddr; + + convertEthToWeth(isEth, _sellAddr, _sellAmt); + _sellAddr.approve(address(oasis), _sellAmt); + + uint _buyAmt = oasis.sellAllAmount( + address(_sellAddr), + _sellAmt, + address(_buyAddr), + _slippageAmt + ); + + isEth = address(_buyAddr) == wethAddr; + + convertWethToEth(isEth, _buyAddr, _buyAmt); + + setUint(setId, _buyAmt); + + _eventName = "LogSell(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); + } +} + +contract ConnectOasis is OasisResolver { + string public name = "Oasis-v1.1"; +} diff --git a/contracts/connectors/uniswap/events.sol b/contracts/connectors/uniswap/events.sol new file mode 100644 index 00000000..293f96f1 --- /dev/null +++ b/contracts/connectors/uniswap/events.sol @@ -0,0 +1,41 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDepositLiquidity( + address indexed tokenA, + address indexed tokenB, + uint amtA, + uint amtB, + uint uniAmount, + uint getId, + uint setId + ); + + event LogWithdrawLiquidity( + address indexed tokenA, + address indexed tokenB, + uint amountA, + uint amountB, + uint uniAmount, + uint getId, + uint[] setId + ); + + event LogBuy( + address indexed buyToken, + address indexed sellToken, + uint256 buyAmt, + uint256 sellAmt, + uint256 getId, + uint256 setId + ); + + event LogSell( + address indexed buyToken, + address indexed sellToken, + uint256 buyAmt, + uint256 sellAmt, + uint256 getId, + uint256 setId + ); +} \ No newline at end of file diff --git a/contracts/connectors/uniswap/helpers.sol b/contracts/connectors/uniswap/helpers.sol new file mode 100644 index 00000000..10569cd8 --- /dev/null +++ b/contracts/connectors/uniswap/helpers.sol @@ -0,0 +1,146 @@ +pragma solidity ^0.7.0; + +import { TokenInterface } from "../../common/interfaces.sol"; +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; +import { IUniswapV2Router02, IUniswapV2Factory } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + + /** + * @dev uniswap v2 router02 + */ + IUniswapV2Router02 internal constant router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); + + function getExpectedBuyAmt( + address[] memory paths, + uint sellAmt + ) internal view returns(uint buyAmt) { + uint[] memory amts = router.getAmountsOut( + sellAmt, + paths + ); + buyAmt = amts[1]; + } + + function getExpectedSellAmt( + address[] memory paths, + uint buyAmt + ) internal view returns(uint sellAmt) { + uint[] memory amts = router.getAmountsIn( + buyAmt, + paths + ); + sellAmt = amts[0]; + } + + function checkPair( + address[] memory paths + ) internal view { + address pair = IUniswapV2Factory(router.factory()).getPair(paths[0], paths[1]); + require(pair != address(0), "No-exchange-address"); + } + + function getPaths( + address buyAddr, + address sellAddr + ) internal pure returns(address[] memory paths) { + paths = new address[](2); + paths[0] = address(sellAddr); + paths[1] = address(buyAddr); + } + + function getMinAmount( + TokenInterface token, + uint amt, + uint slippage + ) internal view returns(uint minAmt) { + uint _amt18 = convertTo18(token.decimals(), amt); + minAmt = wmul(_amt18, sub(WAD, slippage)); + minAmt = convert18ToDec(token.decimals(), minAmt); + } + + function _addLiquidity( + address tokenA, + address tokenB, + uint _amt, + uint unitAmt, + uint slippage + ) internal returns (uint _amtA, uint _amtB, uint _liquidity) { + (TokenInterface _tokenA, TokenInterface _tokenB) = changeEthAddress(tokenA, tokenB); + + _amtA = _amt == uint(-1) ? getTokenBal(TokenInterface(tokenA)) : _amt; + _amtB = convert18ToDec(_tokenB.decimals(), wmul(unitAmt, convertTo18(_tokenA.decimals(), _amtA))); + + bool isEth = address(_tokenA) == wethAddr; + convertEthToWeth(isEth, _tokenA, _amtA); + + isEth = address(_tokenB) == wethAddr; + convertEthToWeth(isEth, _tokenB, _amtB); + + _tokenA.approve(address(router), _amtA); + _tokenB.approve(address(router), _amtB); + + 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, + address(this), + block.timestamp + 1 + ); + } + + function _removeLiquidity( + address tokenA, + address tokenB, + uint _amt, + uint unitAmtA, + uint unitAmtB + ) internal returns (uint _amtA, uint _amtB, uint _uniAmt) { + TokenInterface _tokenA; + TokenInterface _tokenB; + (_tokenA, _tokenB, _uniAmt) = _getRemoveLiquidityData( + tokenA, + tokenB, + _amt + ); + { + uint minAmtA = convert18ToDec(_tokenA.decimals(), wmul(unitAmtA, _uniAmt)); + uint minAmtB = convert18ToDec(_tokenB.decimals(), wmul(unitAmtB, _uniAmt)); + (_amtA, _amtB) = router.removeLiquidity( + address(_tokenA), + address(_tokenB), + _uniAmt, + minAmtA, + minAmtB, + address(this), + block.timestamp + 1 + ); + } + + bool isEth = address(_tokenA) == wethAddr; + convertWethToEth(isEth, _tokenA, _amtA); + + isEth = address(_tokenB) == wethAddr; + convertWethToEth(isEth, _tokenB, _amtB); + } + + function _getRemoveLiquidityData( + address tokenA, + address tokenB, + uint _amt + ) internal returns (TokenInterface _tokenA, TokenInterface _tokenB, uint _uniAmt) { + (_tokenA, _tokenB) = changeEthAddress(tokenA, tokenB); + address exchangeAddr = IUniswapV2Factory(router.factory()).getPair(address(_tokenA), address(_tokenB)); + require(exchangeAddr != address(0), "pair-not-found."); + + TokenInterface uniToken = TokenInterface(exchangeAddr); + _uniAmt = _amt == uint(-1) ? uniToken.balanceOf(address(this)) : _amt; + uniToken.approve(address(router), _uniAmt); + } +} \ No newline at end of file diff --git a/contracts/connectors/uniswap/interface.sol b/contracts/connectors/uniswap/interface.sol new file mode 100644 index 00000000..dc8d904d --- /dev/null +++ b/contracts/connectors/uniswap/interface.sol @@ -0,0 +1,57 @@ +pragma solidity ^0.7.0; + +interface IUniswapV2Router02 { + 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 removeLiquidity( + address tokenA, + address tokenB, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint amountA, uint amountB); + 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 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 { + 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 feeTo() external view returns (address); + function feeToSetter() external view returns (address); + + function createPair(address tokenA, address tokenB) external returns (address pair); +} diff --git a/contracts/connectors/uniswap/main.sol b/contracts/connectors/uniswap/main.sol new file mode 100644 index 00000000..880a2d6b --- /dev/null +++ b/contracts/connectors/uniswap/main.sol @@ -0,0 +1,183 @@ +pragma solidity ^0.7.0; + +import { TokenInterface } from "../../common/interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract UniswapResolver is Helpers, Events { + /** + * @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 returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amtA); + + (uint _amtA, uint _amtB, uint _uniAmt) = _addLiquidity( + tokenA, + tokenB, + _amt, + unitAmt, + slippage + ); + setUint(setId, _uniAmt); + + _eventName = "LogDepositLiquidity(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setId); + } + + /** + * @dev Withdraw Liquidity. + * @param tokenA tokenA address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB tokenB address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param uniAmt uni token amount. + * @param unitAmtA unit amount of amtA/uniAmt with slippage. + * @param unitAmtB unit amount of amtB/uniAmt with slippage. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setIds Set token amounts at this IDs in `InstaMemory` Contract. + */ + function withdraw( + address tokenA, + address tokenB, + uint uniAmt, + uint unitAmtA, + uint unitAmtB, + uint getId, + uint[] calldata setIds + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, uniAmt); + + (uint _amtA, uint _amtB, uint _uniAmt) = _removeLiquidity( + tokenA, + tokenB, + _amt, + unitAmtA, + unitAmtB + ); + + setUint(setIds[0], _amtA); + setUint(setIds[1], _amtB); + + _eventName = "LogWithdrawLiquidity(address,address,uint256,uint256,uint256,uint256[])"; + _eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setIds); + } + + /** + * @dev Buy ETH/ERC20_Token. + * @param buyAddr buying token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr selling token amount.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param buyAmt buying token amount. + * @param unitAmt unit amount of sellAmt/buyAmt with slippage. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function buy( + address buyAddr, + address sellAddr, + uint buyAmt, + uint unitAmt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _buyAmt = getUint(getId, buyAmt); + (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeEthAddress(buyAddr, sellAddr); + address[] memory paths = getPaths(address(_buyAddr), address(_sellAddr)); + + uint _slippageAmt = convert18ToDec(_sellAddr.decimals(), + wmul(unitAmt, convertTo18(_buyAddr.decimals(), _buyAmt))); + + checkPair(paths); + uint _expectedAmt = getExpectedSellAmt(paths, _buyAmt); + require(_slippageAmt >= _expectedAmt, "Too much slippage"); + + bool isEth = address(_sellAddr) == wethAddr; + convertEthToWeth(isEth, _sellAddr, _expectedAmt); + _sellAddr.approve(address(router), _expectedAmt); + + uint _sellAmt = router.swapTokensForExactTokens( + _buyAmt, + _expectedAmt, + paths, + address(this), + block.timestamp + 1 + )[0]; + + isEth = address(_buyAddr) == wethAddr; + convertWethToEth(isEth, _buyAddr, _buyAmt); + + setUint(setId, _sellAmt); + + _eventName = "LogBuy(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); + } + + /** + * @dev Sell ETH/ERC20_Token. + * @param buyAddr buying token address.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr selling token amount.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAmt selling token amount. + * @param unitAmt unit amount of buyAmt/sellAmt with slippage. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function sell( + address buyAddr, + address sellAddr, + uint sellAmt, + uint unitAmt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _sellAmt = getUint(getId, sellAmt); + (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeEthAddress(buyAddr, sellAddr); + address[] memory paths = getPaths(address(_buyAddr), address(_sellAddr)); + + if (_sellAmt == uint(-1)) { + _sellAmt = sellAddr == ethAddr ? address(this).balance : _sellAddr.balanceOf(address(this)); + } + + uint _slippageAmt = convert18ToDec(_buyAddr.decimals(), + wmul(unitAmt, convertTo18(_sellAddr.decimals(), _sellAmt))); + + checkPair(paths); + uint _expectedAmt = getExpectedBuyAmt(paths, _sellAmt); + require(_slippageAmt <= _expectedAmt, "Too much slippage"); + + bool isEth = address(_sellAddr) == wethAddr; + convertEthToWeth(isEth, _sellAddr, _sellAmt); + _sellAddr.approve(address(router), _sellAmt); + + uint _buyAmt = router.swapExactTokensForTokens( + _sellAmt, + _expectedAmt, + paths, + address(this), + block.timestamp + 1 + )[1]; + + isEth = address(_buyAddr) == wethAddr; + convertWethToEth(isEth, _buyAddr, _buyAmt); + + setUint(setId, _buyAmt); + + _eventName = "LogSell(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); + } +} + +contract ConnectUniswapV2 is UniswapResolver { + string public name = "UniswapV2-v1"; +}