From cb56f23f493839421e36927a3bd9c57b9a0c1237 Mon Sep 17 00:00:00 2001 From: bhavik-m <bhavikmehta2001@gmail.com> Date: Tue, 4 Jan 2022 21:25:53 +0530 Subject: [PATCH 1/8] initial commit --- .../arbitrum/connectors/sushiswap/events.sol | 41 ++++ .../arbitrum/connectors/sushiswap/helpers.sol | 184 ++++++++++++++++ .../connectors/sushiswap/interface.sol | 57 +++++ .../arbitrum/connectors/sushiswap/main.sol | 196 ++++++++++++++++++ .../polygon/connectors/sushiswap/events.sol | 41 ++++ .../polygon/connectors/sushiswap/helpers.sol | 184 ++++++++++++++++ .../connectors/sushiswap/interface.sol | 57 +++++ .../polygon/connectors/sushiswap/main.sol | 196 ++++++++++++++++++ 8 files changed, 956 insertions(+) create mode 100644 contracts/arbitrum/connectors/sushiswap/events.sol create mode 100644 contracts/arbitrum/connectors/sushiswap/helpers.sol create mode 100644 contracts/arbitrum/connectors/sushiswap/interface.sol create mode 100644 contracts/arbitrum/connectors/sushiswap/main.sol create mode 100644 contracts/polygon/connectors/sushiswap/events.sol create mode 100644 contracts/polygon/connectors/sushiswap/helpers.sol create mode 100644 contracts/polygon/connectors/sushiswap/interface.sol create mode 100644 contracts/polygon/connectors/sushiswap/main.sol diff --git a/contracts/arbitrum/connectors/sushiswap/events.sol b/contracts/arbitrum/connectors/sushiswap/events.sol new file mode 100644 index 00000000..2717b0a3 --- /dev/null +++ b/contracts/arbitrum/connectors/sushiswap/events.sol @@ -0,0 +1,41 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDepositLiquidity( + address indexed tokenA, + address indexed tokenB, + uint256 amtA, + uint256 amtB, + uint256 uniAmount, + uint256 getId, + uint256 setId + ); + + event LogWithdrawLiquidity( + address indexed tokenA, + address indexed tokenB, + uint256 amountA, + uint256 amountB, + uint256 uniAmount, + uint256 getId, + uint256[] 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/arbitrum/connectors/sushiswap/helpers.sol b/contracts/arbitrum/connectors/sushiswap/helpers.sol new file mode 100644 index 00000000..ea29a03f --- /dev/null +++ b/contracts/arbitrum/connectors/sushiswap/helpers.sol @@ -0,0 +1,184 @@ +pragma solidity ^0.7.0; + +import {TokenInterface} from "../../common/interfaces.sol"; +import {DSMath} from "../../common/math.sol"; +import {Basic} from "../../common/basic.sol"; +import {ISushiSwapRouter, ISushiSwapFactory} from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev ISushiSwapRouter + */ + ISushiSwapRouter internal constant router = + ISushiSwapRouter(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F); + + function getExpectedBuyAmt(address[] memory paths, uint256 sellAmt) + internal + view + returns (uint256 buyAmt) + { + uint256[] memory amts = router.getAmountsOut(sellAmt, paths); + buyAmt = amts[1]; + } + + function getExpectedSellAmt(address[] memory paths, uint256 buyAmt) + internal + view + returns (uint256 sellAmt) + { + uint256[] memory amts = router.getAmountsIn(buyAmt, paths); + sellAmt = amts[0]; + } + + function checkPair(address[] memory paths) internal view { + address pair = ISushiSwapFactory(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, + uint256 amt, + uint256 slippage + ) internal view returns (uint256 minAmt) { + uint256 _amt18 = convertTo18(token.decimals(), amt); + minAmt = wmul(_amt18, sub(WAD, slippage)); + minAmt = convert18ToDec(token.decimals(), minAmt); + } + + function _addLiquidity( + address tokenA, + address tokenB, + uint256 _amt, + uint256 unitAmt, + uint256 slippage + ) + internal + returns ( + uint256 _amtA, + uint256 _amtB, + uint256 _liquidity + ) + { + (TokenInterface _tokenA, TokenInterface _tokenB) = changeEthAddress( + tokenA, + tokenB + ); + + _amtA = _amt == uint256(-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); + + approve(_tokenA, address(router), _amtA); + approve(_tokenB, address(router), _amtB); + + uint256 minAmtA = getMinAmount(_tokenA, _amtA, slippage); + uint256 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, + uint256 _amt, + uint256 unitAmtA, + uint256 unitAmtB + ) + internal + returns ( + uint256 _amtA, + uint256 _amtB, + uint256 _uniAmt + ) + { + TokenInterface _tokenA; + TokenInterface _tokenB; + (_tokenA, _tokenB, _uniAmt) = _getRemoveLiquidityData( + tokenA, + tokenB, + _amt + ); + { + uint256 minAmtA = convert18ToDec( + _tokenA.decimals(), + wmul(unitAmtA, _uniAmt) + ); + uint256 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, + uint256 _amt + ) + internal + returns ( + TokenInterface _tokenA, + TokenInterface _tokenB, + uint256 _uniAmt + ) + { + (_tokenA, _tokenB) = changeEthAddress(tokenA, tokenB); + address exchangeAddr = ISushiSwapFactory(router.factory()).getPair( + address(_tokenA), + address(_tokenB) + ); + require(exchangeAddr != address(0), "pair-not-found."); + + TokenInterface uniToken = TokenInterface(exchangeAddr); + _uniAmt = _amt == uint256(-1) + ? uniToken.balanceOf(address(this)) + : _amt; + approve(uniToken, address(router), _uniAmt); + } +} diff --git a/contracts/arbitrum/connectors/sushiswap/interface.sol b/contracts/arbitrum/connectors/sushiswap/interface.sol new file mode 100644 index 00000000..bd3be483 --- /dev/null +++ b/contracts/arbitrum/connectors/sushiswap/interface.sol @@ -0,0 +1,57 @@ +pragma solidity ^0.7.0; + +interface ISushiSwapRouter { + 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 ISushiSwapFactory { + 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/arbitrum/connectors/sushiswap/main.sol b/contracts/arbitrum/connectors/sushiswap/main.sol new file mode 100644 index 00000000..bd0078bc --- /dev/null +++ b/contracts/arbitrum/connectors/sushiswap/main.sol @@ -0,0 +1,196 @@ +pragma solidity ^0.7.0; + +/** + * @title SushiSwap. + * @dev Decentralized Exchange. + */ + +import { TokenInterface } from "../../common/interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract SushipswapResolver is Helpers, Events { + /** + * @dev Deposit Liquidity. + * @notice Deposit Liquidity to a SushiSwap pool. + * @param tokenA The address of token A.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB The address of token B.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amtA The amount of A tokens to deposit. + * @param unitAmt The unit amount of of amtB/amtA with slippage. + * @param slippage Slippage amount. + * @param getId ID to retrieve amtA. + * @param setId ID stores the amount of pools tokens received. + */ + function deposit( + address tokenA, + address tokenB, + uint256 amtA, + uint256 unitAmt, + uint256 slippage, + uint256 getId, + uint256 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,uint256)"; + _eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setId); + } + + /** + * @dev Withdraw Liquidity. + * @notice Withdraw Liquidity from a SushiSwap pool. + * @param tokenA The address of token A.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB The address of token B.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param uniAmt The amount of pool tokens to withdraw. + * @param unitAmtA The unit amount of amtA/uniAmt with slippage. + * @param unitAmtB The unit amount of amtB/uniAmt with slippage. + * @param getId ID to retrieve uniAmt. + * @param setIds Array of IDs to store the amount tokens received. + */ + function withdraw( + address tokenA, + address tokenB, + uint256 uniAmt, + uint256 unitAmtA, + uint256 unitAmtB, + uint256 getId, + uint256[] 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,uint256[])"; + _eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setIds); + } + + /** + * @dev Buy ETH/ERC20_Token. + * @notice Buy a token using a SushiSwap + * @param buyAddr The address of the token to buy.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param buyAmt The amount of tokens to buy. + * @param unitAmt The unit amount of sellAmt/buyAmt with slippage. + * @param getId ID to retrieve buyAmt. + * @param setId ID to store the amount of tokens sold. + */ + function buy( + address buyAddr, + address sellAddr, + uint256 buyAmt, + uint256 unitAmt, + uint256 getId, + uint256 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); + approve(_sellAddr, 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. + * @notice Sell a token using a SushiSwap + * @param buyAddr The address of the token to buy.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAmt The amount of the token to sell. + * @param unitAmt The unit amount of buyAmt/sellAmt with slippage. + * @param getId ID to retrieve sellAmt. + * @param setId ID stores the amount of token brought. + */ + function sell( + address buyAddr, + address sellAddr, + uint256 sellAmt, + uint256 unitAmt, + uint256 getId, + uint256 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); + approve(_sellAddr, 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 ConnectV2Sushiswap is SushipswapResolver { + string public constant name = "Sushipswap-v1.1"; +} diff --git a/contracts/polygon/connectors/sushiswap/events.sol b/contracts/polygon/connectors/sushiswap/events.sol new file mode 100644 index 00000000..2717b0a3 --- /dev/null +++ b/contracts/polygon/connectors/sushiswap/events.sol @@ -0,0 +1,41 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogDepositLiquidity( + address indexed tokenA, + address indexed tokenB, + uint256 amtA, + uint256 amtB, + uint256 uniAmount, + uint256 getId, + uint256 setId + ); + + event LogWithdrawLiquidity( + address indexed tokenA, + address indexed tokenB, + uint256 amountA, + uint256 amountB, + uint256 uniAmount, + uint256 getId, + uint256[] 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/polygon/connectors/sushiswap/helpers.sol b/contracts/polygon/connectors/sushiswap/helpers.sol new file mode 100644 index 00000000..ea29a03f --- /dev/null +++ b/contracts/polygon/connectors/sushiswap/helpers.sol @@ -0,0 +1,184 @@ +pragma solidity ^0.7.0; + +import {TokenInterface} from "../../common/interfaces.sol"; +import {DSMath} from "../../common/math.sol"; +import {Basic} from "../../common/basic.sol"; +import {ISushiSwapRouter, ISushiSwapFactory} from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev ISushiSwapRouter + */ + ISushiSwapRouter internal constant router = + ISushiSwapRouter(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F); + + function getExpectedBuyAmt(address[] memory paths, uint256 sellAmt) + internal + view + returns (uint256 buyAmt) + { + uint256[] memory amts = router.getAmountsOut(sellAmt, paths); + buyAmt = amts[1]; + } + + function getExpectedSellAmt(address[] memory paths, uint256 buyAmt) + internal + view + returns (uint256 sellAmt) + { + uint256[] memory amts = router.getAmountsIn(buyAmt, paths); + sellAmt = amts[0]; + } + + function checkPair(address[] memory paths) internal view { + address pair = ISushiSwapFactory(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, + uint256 amt, + uint256 slippage + ) internal view returns (uint256 minAmt) { + uint256 _amt18 = convertTo18(token.decimals(), amt); + minAmt = wmul(_amt18, sub(WAD, slippage)); + minAmt = convert18ToDec(token.decimals(), minAmt); + } + + function _addLiquidity( + address tokenA, + address tokenB, + uint256 _amt, + uint256 unitAmt, + uint256 slippage + ) + internal + returns ( + uint256 _amtA, + uint256 _amtB, + uint256 _liquidity + ) + { + (TokenInterface _tokenA, TokenInterface _tokenB) = changeEthAddress( + tokenA, + tokenB + ); + + _amtA = _amt == uint256(-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); + + approve(_tokenA, address(router), _amtA); + approve(_tokenB, address(router), _amtB); + + uint256 minAmtA = getMinAmount(_tokenA, _amtA, slippage); + uint256 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, + uint256 _amt, + uint256 unitAmtA, + uint256 unitAmtB + ) + internal + returns ( + uint256 _amtA, + uint256 _amtB, + uint256 _uniAmt + ) + { + TokenInterface _tokenA; + TokenInterface _tokenB; + (_tokenA, _tokenB, _uniAmt) = _getRemoveLiquidityData( + tokenA, + tokenB, + _amt + ); + { + uint256 minAmtA = convert18ToDec( + _tokenA.decimals(), + wmul(unitAmtA, _uniAmt) + ); + uint256 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, + uint256 _amt + ) + internal + returns ( + TokenInterface _tokenA, + TokenInterface _tokenB, + uint256 _uniAmt + ) + { + (_tokenA, _tokenB) = changeEthAddress(tokenA, tokenB); + address exchangeAddr = ISushiSwapFactory(router.factory()).getPair( + address(_tokenA), + address(_tokenB) + ); + require(exchangeAddr != address(0), "pair-not-found."); + + TokenInterface uniToken = TokenInterface(exchangeAddr); + _uniAmt = _amt == uint256(-1) + ? uniToken.balanceOf(address(this)) + : _amt; + approve(uniToken, address(router), _uniAmt); + } +} diff --git a/contracts/polygon/connectors/sushiswap/interface.sol b/contracts/polygon/connectors/sushiswap/interface.sol new file mode 100644 index 00000000..bd3be483 --- /dev/null +++ b/contracts/polygon/connectors/sushiswap/interface.sol @@ -0,0 +1,57 @@ +pragma solidity ^0.7.0; + +interface ISushiSwapRouter { + 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 ISushiSwapFactory { + 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/polygon/connectors/sushiswap/main.sol b/contracts/polygon/connectors/sushiswap/main.sol new file mode 100644 index 00000000..bd0078bc --- /dev/null +++ b/contracts/polygon/connectors/sushiswap/main.sol @@ -0,0 +1,196 @@ +pragma solidity ^0.7.0; + +/** + * @title SushiSwap. + * @dev Decentralized Exchange. + */ + +import { TokenInterface } from "../../common/interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract SushipswapResolver is Helpers, Events { + /** + * @dev Deposit Liquidity. + * @notice Deposit Liquidity to a SushiSwap pool. + * @param tokenA The address of token A.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB The address of token B.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amtA The amount of A tokens to deposit. + * @param unitAmt The unit amount of of amtB/amtA with slippage. + * @param slippage Slippage amount. + * @param getId ID to retrieve amtA. + * @param setId ID stores the amount of pools tokens received. + */ + function deposit( + address tokenA, + address tokenB, + uint256 amtA, + uint256 unitAmt, + uint256 slippage, + uint256 getId, + uint256 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,uint256)"; + _eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setId); + } + + /** + * @dev Withdraw Liquidity. + * @notice Withdraw Liquidity from a SushiSwap pool. + * @param tokenA The address of token A.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB The address of token B.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param uniAmt The amount of pool tokens to withdraw. + * @param unitAmtA The unit amount of amtA/uniAmt with slippage. + * @param unitAmtB The unit amount of amtB/uniAmt with slippage. + * @param getId ID to retrieve uniAmt. + * @param setIds Array of IDs to store the amount tokens received. + */ + function withdraw( + address tokenA, + address tokenB, + uint256 uniAmt, + uint256 unitAmtA, + uint256 unitAmtB, + uint256 getId, + uint256[] 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,uint256[])"; + _eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setIds); + } + + /** + * @dev Buy ETH/ERC20_Token. + * @notice Buy a token using a SushiSwap + * @param buyAddr The address of the token to buy.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param buyAmt The amount of tokens to buy. + * @param unitAmt The unit amount of sellAmt/buyAmt with slippage. + * @param getId ID to retrieve buyAmt. + * @param setId ID to store the amount of tokens sold. + */ + function buy( + address buyAddr, + address sellAddr, + uint256 buyAmt, + uint256 unitAmt, + uint256 getId, + uint256 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); + approve(_sellAddr, 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. + * @notice Sell a token using a SushiSwap + * @param buyAddr The address of the token to buy.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAmt The amount of the token to sell. + * @param unitAmt The unit amount of buyAmt/sellAmt with slippage. + * @param getId ID to retrieve sellAmt. + * @param setId ID stores the amount of token brought. + */ + function sell( + address buyAddr, + address sellAddr, + uint256 sellAmt, + uint256 unitAmt, + uint256 getId, + uint256 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); + approve(_sellAddr, 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 ConnectV2Sushiswap is SushipswapResolver { + string public constant name = "Sushipswap-v1.1"; +} From d1e4b028d65bf1c69c1d6904f85427ee001a2beb Mon Sep 17 00:00:00 2001 From: bhavik-m <bhavikmehta2001@gmail.com> Date: Wed, 5 Jan 2022 13:12:35 +0530 Subject: [PATCH 2/8] added connector on polygon and arbitrum --- .../arbitrum/connectors/sushiswap/helpers.sol | 2 +- .../arbitrum/connectors/sushiswap/main.sol | 4 +- .../polygon/connectors/sushiswap/helpers.sol | 22 ++++----- .../polygon/connectors/sushiswap/main.sol | 46 +++++++++---------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/contracts/arbitrum/connectors/sushiswap/helpers.sol b/contracts/arbitrum/connectors/sushiswap/helpers.sol index ea29a03f..ae67bdd7 100644 --- a/contracts/arbitrum/connectors/sushiswap/helpers.sol +++ b/contracts/arbitrum/connectors/sushiswap/helpers.sol @@ -10,7 +10,7 @@ abstract contract Helpers is DSMath, Basic { * @dev ISushiSwapRouter */ ISushiSwapRouter internal constant router = - ISushiSwapRouter(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F); + ISushiSwapRouter(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506); function getExpectedBuyAmt(address[] memory paths, uint256 sellAmt) internal diff --git a/contracts/arbitrum/connectors/sushiswap/main.sol b/contracts/arbitrum/connectors/sushiswap/main.sol index bd0078bc..b7716406 100644 --- a/contracts/arbitrum/connectors/sushiswap/main.sol +++ b/contracts/arbitrum/connectors/sushiswap/main.sol @@ -191,6 +191,6 @@ abstract contract SushipswapResolver is Helpers, Events { } } -contract ConnectV2Sushiswap is SushipswapResolver { - string public constant name = "Sushipswap-v1.1"; +contract ConnectV2SushiswapArbitrum is SushipswapResolver { + string public constant name = "Sushiswap-v1.1"; } diff --git a/contracts/polygon/connectors/sushiswap/helpers.sol b/contracts/polygon/connectors/sushiswap/helpers.sol index ea29a03f..9f43f2b6 100644 --- a/contracts/polygon/connectors/sushiswap/helpers.sol +++ b/contracts/polygon/connectors/sushiswap/helpers.sol @@ -10,7 +10,7 @@ abstract contract Helpers is DSMath, Basic { * @dev ISushiSwapRouter */ ISushiSwapRouter internal constant router = - ISushiSwapRouter(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F); + ISushiSwapRouter(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506); function getExpectedBuyAmt(address[] memory paths, uint256 sellAmt) internal @@ -72,7 +72,7 @@ abstract contract Helpers is DSMath, Basic { uint256 _liquidity ) { - (TokenInterface _tokenA, TokenInterface _tokenB) = changeEthAddress( + (TokenInterface _tokenA, TokenInterface _tokenB) = changeMaticAddress( tokenA, tokenB ); @@ -85,11 +85,11 @@ abstract contract Helpers is DSMath, Basic { wmul(unitAmt, convertTo18(_tokenA.decimals(), _amtA)) ); - bool isEth = address(_tokenA) == wethAddr; - convertEthToWeth(isEth, _tokenA, _amtA); + bool isMatic = address(_tokenA) == wmaticAddr; + convertMaticToWmatic(isMatic, _tokenA, _amtA); - isEth = address(_tokenB) == wethAddr; - convertEthToWeth(isEth, _tokenB, _amtB); + isMatic = address(_tokenB) == wmaticAddr; + convertMaticToWmatic(isMatic, _tokenB, _amtB); approve(_tokenA, address(router), _amtA); approve(_tokenB, address(router), _amtB); @@ -149,11 +149,11 @@ abstract contract Helpers is DSMath, Basic { ); } - bool isEth = address(_tokenA) == wethAddr; - convertWethToEth(isEth, _tokenA, _amtA); + bool isMatic = address(_tokenA) == wmaticAddr; + convertWmaticToMatic(isMatic, _tokenA, _amtA); - isEth = address(_tokenB) == wethAddr; - convertWethToEth(isEth, _tokenB, _amtB); + isMatic = address(_tokenB) == wmaticAddr; + convertWmaticToMatic(isMatic, _tokenB, _amtB); } function _getRemoveLiquidityData( @@ -168,7 +168,7 @@ abstract contract Helpers is DSMath, Basic { uint256 _uniAmt ) { - (_tokenA, _tokenB) = changeEthAddress(tokenA, tokenB); + (_tokenA, _tokenB) = changeMaticAddress(tokenA, tokenB); address exchangeAddr = ISushiSwapFactory(router.factory()).getPair( address(_tokenA), address(_tokenB) diff --git a/contracts/polygon/connectors/sushiswap/main.sol b/contracts/polygon/connectors/sushiswap/main.sol index bd0078bc..a4647769 100644 --- a/contracts/polygon/connectors/sushiswap/main.sol +++ b/contracts/polygon/connectors/sushiswap/main.sol @@ -13,8 +13,8 @@ abstract contract SushipswapResolver is Helpers, Events { /** * @dev Deposit Liquidity. * @notice Deposit Liquidity to a SushiSwap pool. - * @param tokenA The address of token A.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) - * @param tokenB The address of token B.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenA The address of token A.(For Matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB The address of token B.(For Matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) * @param amtA The amount of A tokens to deposit. * @param unitAmt The unit amount of of amtB/amtA with slippage. * @param slippage Slippage amount. @@ -48,8 +48,8 @@ abstract contract SushipswapResolver is Helpers, Events { /** * @dev Withdraw Liquidity. * @notice Withdraw Liquidity from a SushiSwap pool. - * @param tokenA The address of token A.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) - * @param tokenB The address of token B.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenA The address of token A.(For Matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB The address of token B.(For Matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) * @param uniAmt The amount of pool tokens to withdraw. * @param unitAmtA The unit amount of amtA/uniAmt with slippage. * @param unitAmtB The unit amount of amtB/uniAmt with slippage. @@ -83,10 +83,10 @@ abstract contract SushipswapResolver is Helpers, Events { } /** - * @dev Buy ETH/ERC20_Token. + * @dev Buy Matic/ERC20_Token. * @notice Buy a token using a SushiSwap - * @param buyAddr The address of the token to buy.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) - * @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param buyAddr The address of the token to buy.(For Matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For Matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) * @param buyAmt The amount of tokens to buy. * @param unitAmt The unit amount of sellAmt/buyAmt with slippage. * @param getId ID to retrieve buyAmt. @@ -101,7 +101,7 @@ abstract contract SushipswapResolver is Helpers, Events { uint256 setId ) external payable returns (string memory _eventName, bytes memory _eventParam) { uint _buyAmt = getUint(getId, buyAmt); - (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeEthAddress(buyAddr, sellAddr); + (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeMaticAddress(buyAddr, sellAddr); address[] memory paths = getPaths(address(_buyAddr), address(_sellAddr)); uint _slippageAmt = convert18ToDec(_sellAddr.decimals(), @@ -112,8 +112,8 @@ abstract contract SushipswapResolver is Helpers, Events { uint _expectedAmt = getExpectedSellAmt(paths, _buyAmt); require(_slippageAmt >= _expectedAmt, "Too much slippage"); - bool isEth = address(_sellAddr) == wethAddr; - convertEthToWeth(isEth, _sellAddr, _expectedAmt); + bool isMatic = address(_sellAddr) == wmaticAddr; + convertMaticToWmatic(isMatic, _sellAddr, _expectedAmt); approve(_sellAddr, address(router), _expectedAmt); uint _sellAmt = router.swapTokensForExactTokens( @@ -124,8 +124,8 @@ abstract contract SushipswapResolver is Helpers, Events { block.timestamp + 1 )[0]; - isEth = address(_buyAddr) == wethAddr; - convertWethToEth(isEth, _buyAddr, _buyAmt); + isMatic = address(_buyAddr) == wmaticAddr; + convertWmaticToMatic(isMatic, _buyAddr, _buyAmt); setUint(setId, _sellAmt); @@ -134,10 +134,10 @@ abstract contract SushipswapResolver is Helpers, Events { } /** - * @dev Sell ETH/ERC20_Token. + * @dev Sell Matic/ERC20_Token. * @notice Sell a token using a SushiSwap - * @param buyAddr The address of the token to buy.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) - * @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param buyAddr The address of the token to buy.(For Matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For Matic: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) * @param sellAmt The amount of the token to sell. * @param unitAmt The unit amount of buyAmt/sellAmt with slippage. * @param getId ID to retrieve sellAmt. @@ -152,11 +152,11 @@ abstract contract SushipswapResolver is Helpers, Events { uint256 setId ) external payable returns (string memory _eventName, bytes memory _eventParam) { uint _sellAmt = getUint(getId, sellAmt); - (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeEthAddress(buyAddr, sellAddr); + (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeMaticAddress(buyAddr, sellAddr); address[] memory paths = getPaths(address(_buyAddr), address(_sellAddr)); if (_sellAmt == uint(-1)) { - _sellAmt = sellAddr == ethAddr ? + _sellAmt = sellAddr == maticAddr ? address(this).balance : _sellAddr.balanceOf(address(this)); } @@ -169,8 +169,8 @@ abstract contract SushipswapResolver is Helpers, Events { uint _expectedAmt = getExpectedBuyAmt(paths, _sellAmt); require(_slippageAmt <= _expectedAmt, "Too much slippage"); - bool isEth = address(_sellAddr) == wethAddr; - convertEthToWeth(isEth, _sellAddr, _sellAmt); + bool isMatic = address(_sellAddr) == wmaticAddr; + convertMaticToWmatic(isMatic, _sellAddr, _sellAmt); approve(_sellAddr, address(router), _sellAmt); uint _buyAmt = router.swapExactTokensForTokens( @@ -181,8 +181,8 @@ abstract contract SushipswapResolver is Helpers, Events { block.timestamp + 1 )[1]; - isEth = address(_buyAddr) == wethAddr; - convertWethToEth(isEth, _buyAddr, _buyAmt); + isMatic = address(_buyAddr) == wmaticAddr; + convertWmaticToMatic(isMatic, _buyAddr, _buyAmt); setUint(setId, _buyAmt); @@ -191,6 +191,6 @@ abstract contract SushipswapResolver is Helpers, Events { } } -contract ConnectV2Sushiswap is SushipswapResolver { - string public constant name = "Sushipswap-v1.1"; +contract ConnectV2SushiswapPolygon is SushipswapResolver { + string public constant name = "Sushiswap-v1.1"; } From a0a3342c822ee07d0cb850e492ba477e77b3be4e Mon Sep 17 00:00:00 2001 From: pradyuman-verma <pradyumnverma27@gmail.com> Date: Wed, 5 Jan 2022 23:44:46 +0530 Subject: [PATCH 3/8] sushiswap polygon tests --- test/polygon/sushiswap/sushiswap.test.ts | 148 +++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 test/polygon/sushiswap/sushiswap.test.ts diff --git a/test/polygon/sushiswap/sushiswap.test.ts b/test/polygon/sushiswap/sushiswap.test.ts new file mode 100644 index 00000000..4314e1b8 --- /dev/null +++ b/test/polygon/sushiswap/sushiswap.test.ts @@ -0,0 +1,148 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; + +import { constants } from "../../../scripts/constant/constant"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SushiswapPolygon__factory, ConnectV2SushiswapPolygon } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +const DAI_ADDR = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; + +describe("Sushiswap", function () { + const connectorName = "Sushiswap-v1"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13005785 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SushiswapPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit MATIC & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + + it("Deposit MATIC & USDT into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + it("Should deposit successfully", async function () { + const maticAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const maticAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [maticAddress, DAI_ADDR, maticAmount, daiUnitAmount, "500000000000000000", getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }).timeout(10000000000); + + it("Should withdraw successfully", async function () { + const maticAmount = ethers.utils.parseEther("0.1"); + const maticAddress = constants.native_address; + + const getId = "0"; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [maticAddress, DAI_ADDR, maticAmount, 0, 0, getId, setIds] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + + it("Should buy successfully", async function () { + const maticAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const maticAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [maticAddress, DAI_ADDR, maticAmount, daiUnitAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + }); +}); From aeae64a15fd56b58eae232a037a518acabab1a0e Mon Sep 17 00:00:00 2001 From: pradyuman-verma <pradyumnverma27@gmail.com> Date: Wed, 5 Jan 2022 23:50:16 +0530 Subject: [PATCH 4/8] sushiswap arbitrum tests --- test/arbitrum/sushiswap/sushiswap.test.ts | 148 ++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 test/arbitrum/sushiswap/sushiswap.test.ts diff --git a/test/arbitrum/sushiswap/sushiswap.test.ts b/test/arbitrum/sushiswap/sushiswap.test.ts new file mode 100644 index 00000000..68da8d66 --- /dev/null +++ b/test/arbitrum/sushiswap/sushiswap.test.ts @@ -0,0 +1,148 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; + +import { constants } from "../../../scripts/constant/constant"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SushiswapArbitrum__factory } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +const DAI_ADDR = "0xda10009cbd5d07dd0cecc66161fc93d7c9000da1"; + +describe("Sushiswap", function () { + const connectorName = "Sushiswap-v1"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13005785 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SushiswapArbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit aeth & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + + it("Deposit aeth & USDT into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + it("Should deposit successfully", async function () { + const aethAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const aethAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [aethAddress, DAI_ADDR, aethAmount, daiUnitAmount, "500000000000000000", getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }).timeout(10000000000); + + it("Should withdraw successfully", async function () { + const aethAmount = ethers.utils.parseEther("0.1"); + const aethAddress = constants.native_address; + + const getId = "0"; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [aethAddress, DAI_ADDR, aethAmount, 0, 0, getId, setIds] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + + it("Should buy successfully", async function () { + const aethAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const aethAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [aethAddress, DAI_ADDR, aethAmount, daiUnitAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + }); +}); From 9f73e96f35b2362e40491fda52ffeb8477e09577 Mon Sep 17 00:00:00 2001 From: pradyuman-verma <pradyumnverma27@gmail.com> Date: Thu, 6 Jan 2022 00:04:33 +0530 Subject: [PATCH 5/8] sushiswap: avalanche --- .../avalanche/connectors/sushiswap/events.sol | 42 ++++ .../connectors/sushiswap/helpers.sol | 185 ++++++++++++++++ .../connectors/sushiswap/interface.sol | 58 ++++++ .../avalanche/connectors/sushiswap/main.sol | 197 ++++++++++++++++++ 4 files changed, 482 insertions(+) create mode 100644 contracts/avalanche/connectors/sushiswap/events.sol create mode 100644 contracts/avalanche/connectors/sushiswap/helpers.sol create mode 100644 contracts/avalanche/connectors/sushiswap/interface.sol create mode 100644 contracts/avalanche/connectors/sushiswap/main.sol diff --git a/contracts/avalanche/connectors/sushiswap/events.sol b/contracts/avalanche/connectors/sushiswap/events.sol new file mode 100644 index 00000000..4697e44c --- /dev/null +++ b/contracts/avalanche/connectors/sushiswap/events.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogDepositLiquidity( + address indexed tokenA, + address indexed tokenB, + uint256 amtA, + uint256 amtB, + uint256 uniAmount, + uint256 getId, + uint256 setId + ); + + event LogWithdrawLiquidity( + address indexed tokenA, + address indexed tokenB, + uint256 amountA, + uint256 amountB, + uint256 uniAmount, + uint256 getId, + uint256[] 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/avalanche/connectors/sushiswap/helpers.sol b/contracts/avalanche/connectors/sushiswap/helpers.sol new file mode 100644 index 00000000..04149201 --- /dev/null +++ b/contracts/avalanche/connectors/sushiswap/helpers.sol @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import {TokenInterface} from "../../common/interfaces.sol"; +import {DSMath} from "../../common/math.sol"; +import {Basic} from "../../common/basic.sol"; +import {ISushiSwapRouter, ISushiSwapFactory} from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev ISushiSwapRouter + */ + ISushiSwapRouter internal constant router = + ISushiSwapRouter(0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506); + + function getExpectedBuyAmt(address[] memory paths, uint256 sellAmt) + internal + view + returns (uint256 buyAmt) + { + uint256[] memory amts = router.getAmountsOut(sellAmt, paths); + buyAmt = amts[1]; + } + + function getExpectedSellAmt(address[] memory paths, uint256 buyAmt) + internal + view + returns (uint256 sellAmt) + { + uint256[] memory amts = router.getAmountsIn(buyAmt, paths); + sellAmt = amts[0]; + } + + function checkPair(address[] memory paths) internal view { + address pair = ISushiSwapFactory(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, + uint256 amt, + uint256 slippage + ) internal view returns (uint256 minAmt) { + uint256 _amt18 = convertTo18(token.decimals(), amt); + minAmt = wmul(_amt18, sub(WAD, slippage)); + minAmt = convert18ToDec(token.decimals(), minAmt); + } + + function _addLiquidity( + address tokenA, + address tokenB, + uint256 _amt, + uint256 unitAmt, + uint256 slippage + ) + internal + returns ( + uint256 _amtA, + uint256 _amtB, + uint256 _liquidity + ) + { + (TokenInterface _tokenA, TokenInterface _tokenB) = changeAvaxAddress( + tokenA, + tokenB + ); + + _amtA = _amt == uint256(-1) + ? getTokenBal(TokenInterface(tokenA)) + : _amt; + _amtB = convert18ToDec( + _tokenB.decimals(), + wmul(unitAmt, convertTo18(_tokenA.decimals(), _amtA)) + ); + + bool isAvax = address(_tokenA) == wavaxAddr; + convertAvaxToWavax(isAvax, _tokenA, _amtA); + + isAvax = address(_tokenB) == wavaxAddr; + convertAvaxToWavax(isAvax, _tokenB, _amtB); + + approve(_tokenA, address(router), _amtA); + approve(_tokenB, address(router), _amtB); + + uint256 minAmtA = getMinAmount(_tokenA, _amtA, slippage); + uint256 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, + uint256 _amt, + uint256 unitAmtA, + uint256 unitAmtB + ) + internal + returns ( + uint256 _amtA, + uint256 _amtB, + uint256 _uniAmt + ) + { + TokenInterface _tokenA; + TokenInterface _tokenB; + (_tokenA, _tokenB, _uniAmt) = _getRemoveLiquidityData( + tokenA, + tokenB, + _amt + ); + { + uint256 minAmtA = convert18ToDec( + _tokenA.decimals(), + wmul(unitAmtA, _uniAmt) + ); + uint256 minAmtB = convert18ToDec( + _tokenB.decimals(), + wmul(unitAmtB, _uniAmt) + ); + (_amtA, _amtB) = router.removeLiquidity( + address(_tokenA), + address(_tokenB), + _uniAmt, + minAmtA, + minAmtB, + address(this), + block.timestamp + 1 + ); + } + + bool isAvax = address(_tokenA) == wavaxAddr; + convertWavaxToAvax(isAvax, _tokenA, _amtA); + + isAvax = address(_tokenB) == wavaxAddr; + convertWavaxToAvax(isAvax, _tokenB, _amtB); + } + + function _getRemoveLiquidityData( + address tokenA, + address tokenB, + uint256 _amt + ) + internal + returns ( + TokenInterface _tokenA, + TokenInterface _tokenB, + uint256 _uniAmt + ) + { + (_tokenA, _tokenB) = changeAvaxAddress(tokenA, tokenB); + address exchangeAddr = ISushiSwapFactory(router.factory()).getPair( + address(_tokenA), + address(_tokenB) + ); + require(exchangeAddr != address(0), "pair-not-found."); + + TokenInterface uniToken = TokenInterface(exchangeAddr); + _uniAmt = _amt == uint256(-1) + ? uniToken.balanceOf(address(this)) + : _amt; + approve(uniToken, address(router), _uniAmt); + } +} diff --git a/contracts/avalanche/connectors/sushiswap/interface.sol b/contracts/avalanche/connectors/sushiswap/interface.sol new file mode 100644 index 00000000..b141c7b5 --- /dev/null +++ b/contracts/avalanche/connectors/sushiswap/interface.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface ISushiSwapRouter { + 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 ISushiSwapFactory { + 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/avalanche/connectors/sushiswap/main.sol b/contracts/avalanche/connectors/sushiswap/main.sol new file mode 100644 index 00000000..8b9ef5e3 --- /dev/null +++ b/contracts/avalanche/connectors/sushiswap/main.sol @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +/** + * @title SushiSwap. + * @dev Decentralized Exchange. + */ + +import { TokenInterface } from "../../common/interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract SushipswapResolver is Helpers, Events { + /** + * @dev Deposit Liquidity. + * @notice Deposit Liquidity to a SushiSwap pool. + * @param tokenA The address of token A.(For Avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB The address of token B.(For Avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amtA The amount of A tokens to deposit. + * @param unitAmt The unit amount of of amtB/amtA with slippage. + * @param slippage Slippage amount. + * @param getId ID to retrieve amtA. + * @param setId ID stores the amount of pools tokens received. + */ + function deposit( + address tokenA, + address tokenB, + uint256 amtA, + uint256 unitAmt, + uint256 slippage, + uint256 getId, + uint256 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,uint256)"; + _eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setId); + } + + /** + * @dev Withdraw Liquidity. + * @notice Withdraw Liquidity from a SushiSwap pool. + * @param tokenA The address of token A.(For Avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param tokenB The address of token B.(For Avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param uniAmt The amount of pool tokens to withdraw. + * @param unitAmtA The unit amount of amtA/uniAmt with slippage. + * @param unitAmtB The unit amount of amtB/uniAmt with slippage. + * @param getId ID to retrieve uniAmt. + * @param setIds Array of IDs to store the amount tokens received. + */ + function withdraw( + address tokenA, + address tokenB, + uint256 uniAmt, + uint256 unitAmtA, + uint256 unitAmtB, + uint256 getId, + uint256[] 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,uint256[])"; + _eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setIds); + } + + /** + * @dev Buy Avax/ERC20_Token. + * @notice Buy a token using a SushiSwap + * @param buyAddr The address of the token to buy.(For Avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For Avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param buyAmt The amount of tokens to buy. + * @param unitAmt The unit amount of sellAmt/buyAmt with slippage. + * @param getId ID to retrieve buyAmt. + * @param setId ID to store the amount of tokens sold. + */ + function buy( + address buyAddr, + address sellAddr, + uint256 buyAmt, + uint256 unitAmt, + uint256 getId, + uint256 setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _buyAmt = getUint(getId, buyAmt); + (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeAvaxAddress(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 isAvax = address(_sellAddr) == wavaxAddr; + convertAvaxToWavax(isAvax, _sellAddr, _expectedAmt); + approve(_sellAddr, address(router), _expectedAmt); + + uint _sellAmt = router.swapTokensForExactTokens( + _buyAmt, + _expectedAmt, + paths, + address(this), + block.timestamp + 1 + )[0]; + + isAvax = address(_buyAddr) == wavaxAddr; + convertWavaxToAvax(isAvax, _buyAddr, _buyAmt); + + setUint(setId, _sellAmt); + + _eventName = "LogBuy(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); + } + + /** + * @dev Sell Avax/ERC20_Token. + * @notice Sell a token using a SushiSwap + * @param buyAddr The address of the token to buy.(For Avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For Avax: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAmt The amount of the token to sell. + * @param unitAmt The unit amount of buyAmt/sellAmt with slippage. + * @param getId ID to retrieve sellAmt. + * @param setId ID stores the amount of token brought. + */ + function sell( + address buyAddr, + address sellAddr, + uint256 sellAmt, + uint256 unitAmt, + uint256 getId, + uint256 setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _sellAmt = getUint(getId, sellAmt); + (TokenInterface _buyAddr, TokenInterface _sellAddr) = changeAvaxAddress(buyAddr, sellAddr); + address[] memory paths = getPaths(address(_buyAddr), address(_sellAddr)); + + if (_sellAmt == uint(-1)) { + _sellAmt = sellAddr == avaxAddr ? + 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 isAvax = address(_sellAddr) == wavaxAddr; + convertAvaxToWavax(isAvax, _sellAddr, _sellAmt); + approve(_sellAddr, address(router), _sellAmt); + + uint _buyAmt = router.swapExactTokensForTokens( + _sellAmt, + _expectedAmt, + paths, + address(this), + block.timestamp + 1 + )[1]; + + isAvax = address(_buyAddr) == wavaxAddr; + convertWavaxToAvax(isAvax, _buyAddr, _buyAmt); + + setUint(setId, _buyAmt); + + _eventName = "LogSell(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId); + } +} + +contract ConnectV2SushiswapAvalanche is SushipswapResolver { + string public constant name = "Sushiswap-v1.1"; +} From e0b165808151b31d4eacbff8c69d666433c22fe4 Mon Sep 17 00:00:00 2001 From: pradyuman-verma <pradyumnverma27@gmail.com> Date: Thu, 6 Jan 2022 00:05:17 +0530 Subject: [PATCH 6/8] added sushiswap avalanche tests --- test/avalanche/sushiswap/sushiswap.test.ts | 148 +++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 test/avalanche/sushiswap/sushiswap.test.ts diff --git a/test/avalanche/sushiswap/sushiswap.test.ts b/test/avalanche/sushiswap/sushiswap.test.ts new file mode 100644 index 00000000..c2ef92fe --- /dev/null +++ b/test/avalanche/sushiswap/sushiswap.test.ts @@ -0,0 +1,148 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; + +import { constants } from "../../../scripts/constant/constant"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SushiswapAvalanche__factory, ConnectV2SushiswapAvalanche } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +const DAI_ADDR = "0xd586e7f844cea2f87f50152665bcbc2c279d8d70"; + +describe("Sushiswap", function () { + const connectorName = "Sushiswap-v1"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13005785 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SushiswapAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit avax & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + + it("Deposit avax & USDT into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + it("Should deposit successfully", async function () { + const avaxAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const avaxAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [avaxAddress, DAI_ADDR, avaxAmount, daiUnitAmount, "500000000000000000", getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }).timeout(10000000000); + + it("Should withdraw successfully", async function () { + const avaxAmount = ethers.utils.parseEther("0.1"); + const avaxAddress = constants.native_address; + + const getId = "0"; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [avaxAddress, DAI_ADDR, avaxAmount, 0, 0, getId, setIds] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + + it("Should buy successfully", async function () { + const avaxAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const avaxAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [avaxAddress, DAI_ADDR, avaxAmount, daiUnitAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + }); +}); From ce83a8bc6e6cc56e0ecd35f0e27b1de19674ef99 Mon Sep 17 00:00:00 2001 From: pradyuman-verma <pradyumnverma27@gmail.com> Date: Tue, 11 Jan 2022 12:40:22 +0530 Subject: [PATCH 7/8] minor fixes --- contracts/arbitrum/connectors/sushiswap/events.sol | 1 + contracts/arbitrum/connectors/sushiswap/helpers.sol | 1 + contracts/arbitrum/connectors/sushiswap/interface.sol | 1 + contracts/arbitrum/connectors/sushiswap/main.sol | 7 ++++--- contracts/avalanche/connectors/sushiswap/main.sol | 6 +++--- contracts/mainnet/connectors/sushiswap/events.sol | 1 + contracts/mainnet/connectors/sushiswap/helpers.sol | 1 + contracts/mainnet/connectors/sushiswap/interface.sol | 1 + contracts/mainnet/connectors/sushiswap/main.sol | 7 ++++--- contracts/polygon/connectors/sushiswap/events.sol | 1 + contracts/polygon/connectors/sushiswap/helpers.sol | 1 + contracts/polygon/connectors/sushiswap/interface.sol | 1 + contracts/polygon/connectors/sushiswap/main.sol | 7 ++++--- 13 files changed, 24 insertions(+), 12 deletions(-) diff --git a/contracts/arbitrum/connectors/sushiswap/events.sol b/contracts/arbitrum/connectors/sushiswap/events.sol index 2717b0a3..4697e44c 100644 --- a/contracts/arbitrum/connectors/sushiswap/events.sol +++ b/contracts/arbitrum/connectors/sushiswap/events.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; contract Events { diff --git a/contracts/arbitrum/connectors/sushiswap/helpers.sol b/contracts/arbitrum/connectors/sushiswap/helpers.sol index ae67bdd7..91a0ca11 100644 --- a/contracts/arbitrum/connectors/sushiswap/helpers.sol +++ b/contracts/arbitrum/connectors/sushiswap/helpers.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; import {TokenInterface} from "../../common/interfaces.sol"; diff --git a/contracts/arbitrum/connectors/sushiswap/interface.sol b/contracts/arbitrum/connectors/sushiswap/interface.sol index bd3be483..b141c7b5 100644 --- a/contracts/arbitrum/connectors/sushiswap/interface.sol +++ b/contracts/arbitrum/connectors/sushiswap/interface.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; interface ISushiSwapRouter { diff --git a/contracts/arbitrum/connectors/sushiswap/main.sol b/contracts/arbitrum/connectors/sushiswap/main.sol index b7716406..f8b002eb 100644 --- a/contracts/arbitrum/connectors/sushiswap/main.sol +++ b/contracts/arbitrum/connectors/sushiswap/main.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** @@ -9,7 +10,7 @@ import { TokenInterface } from "../../common/interfaces.sol"; import { Helpers } from "./helpers.sol"; import { Events } from "./events.sol"; -abstract contract SushipswapResolver is Helpers, Events { +abstract contract SushiswapResolver is Helpers, Events { /** * @dev Deposit Liquidity. * @notice Deposit Liquidity to a SushiSwap pool. @@ -191,6 +192,6 @@ abstract contract SushipswapResolver is Helpers, Events { } } -contract ConnectV2SushiswapArbitrum is SushipswapResolver { - string public constant name = "Sushiswap-v1.1"; +contract ConnectV2SushiswapArbitrum is SushiswapResolver { + string public constant name = "Sushiswap-v1"; } diff --git a/contracts/avalanche/connectors/sushiswap/main.sol b/contracts/avalanche/connectors/sushiswap/main.sol index 8b9ef5e3..f8ed4847 100644 --- a/contracts/avalanche/connectors/sushiswap/main.sol +++ b/contracts/avalanche/connectors/sushiswap/main.sol @@ -10,7 +10,7 @@ import { TokenInterface } from "../../common/interfaces.sol"; import { Helpers } from "./helpers.sol"; import { Events } from "./events.sol"; -abstract contract SushipswapResolver is Helpers, Events { +abstract contract SushiswapResolver is Helpers, Events { /** * @dev Deposit Liquidity. * @notice Deposit Liquidity to a SushiSwap pool. @@ -192,6 +192,6 @@ abstract contract SushipswapResolver is Helpers, Events { } } -contract ConnectV2SushiswapAvalanche is SushipswapResolver { - string public constant name = "Sushiswap-v1.1"; +contract ConnectV2SushiswapAvalanche is SushiswapResolver { + string public constant name = "Sushiswap-v1"; } diff --git a/contracts/mainnet/connectors/sushiswap/events.sol b/contracts/mainnet/connectors/sushiswap/events.sol index 2717b0a3..4697e44c 100644 --- a/contracts/mainnet/connectors/sushiswap/events.sol +++ b/contracts/mainnet/connectors/sushiswap/events.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; contract Events { diff --git a/contracts/mainnet/connectors/sushiswap/helpers.sol b/contracts/mainnet/connectors/sushiswap/helpers.sol index ea29a03f..9260b080 100644 --- a/contracts/mainnet/connectors/sushiswap/helpers.sol +++ b/contracts/mainnet/connectors/sushiswap/helpers.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; import {TokenInterface} from "../../common/interfaces.sol"; diff --git a/contracts/mainnet/connectors/sushiswap/interface.sol b/contracts/mainnet/connectors/sushiswap/interface.sol index bd3be483..b141c7b5 100644 --- a/contracts/mainnet/connectors/sushiswap/interface.sol +++ b/contracts/mainnet/connectors/sushiswap/interface.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; interface ISushiSwapRouter { diff --git a/contracts/mainnet/connectors/sushiswap/main.sol b/contracts/mainnet/connectors/sushiswap/main.sol index bd0078bc..bd559688 100644 --- a/contracts/mainnet/connectors/sushiswap/main.sol +++ b/contracts/mainnet/connectors/sushiswap/main.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** @@ -9,7 +10,7 @@ import { TokenInterface } from "../../common/interfaces.sol"; import { Helpers } from "./helpers.sol"; import { Events } from "./events.sol"; -abstract contract SushipswapResolver is Helpers, Events { +abstract contract SushiswapResolver is Helpers, Events { /** * @dev Deposit Liquidity. * @notice Deposit Liquidity to a SushiSwap pool. @@ -191,6 +192,6 @@ abstract contract SushipswapResolver is Helpers, Events { } } -contract ConnectV2Sushiswap is SushipswapResolver { - string public constant name = "Sushipswap-v1.1"; +contract ConnectV2Sushiswap is SushiswapResolver { + string public constant name = "Sushiswap-v1"; } diff --git a/contracts/polygon/connectors/sushiswap/events.sol b/contracts/polygon/connectors/sushiswap/events.sol index 2717b0a3..4697e44c 100644 --- a/contracts/polygon/connectors/sushiswap/events.sol +++ b/contracts/polygon/connectors/sushiswap/events.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; contract Events { diff --git a/contracts/polygon/connectors/sushiswap/helpers.sol b/contracts/polygon/connectors/sushiswap/helpers.sol index 9f43f2b6..5a03a061 100644 --- a/contracts/polygon/connectors/sushiswap/helpers.sol +++ b/contracts/polygon/connectors/sushiswap/helpers.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; import {TokenInterface} from "../../common/interfaces.sol"; diff --git a/contracts/polygon/connectors/sushiswap/interface.sol b/contracts/polygon/connectors/sushiswap/interface.sol index bd3be483..b141c7b5 100644 --- a/contracts/polygon/connectors/sushiswap/interface.sol +++ b/contracts/polygon/connectors/sushiswap/interface.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; interface ISushiSwapRouter { diff --git a/contracts/polygon/connectors/sushiswap/main.sol b/contracts/polygon/connectors/sushiswap/main.sol index a4647769..5f80f946 100644 --- a/contracts/polygon/connectors/sushiswap/main.sol +++ b/contracts/polygon/connectors/sushiswap/main.sol @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** @@ -9,7 +10,7 @@ import { TokenInterface } from "../../common/interfaces.sol"; import { Helpers } from "./helpers.sol"; import { Events } from "./events.sol"; -abstract contract SushipswapResolver is Helpers, Events { +abstract contract SushiswapResolver is Helpers, Events { /** * @dev Deposit Liquidity. * @notice Deposit Liquidity to a SushiSwap pool. @@ -191,6 +192,6 @@ abstract contract SushipswapResolver is Helpers, Events { } } -contract ConnectV2SushiswapPolygon is SushipswapResolver { - string public constant name = "Sushiswap-v1.1"; +contract ConnectV2SushiswapPolygon is SushiswapResolver { + string public constant name = "Sushiswap-v1"; } From 744638ee5d8df7493edf6e5efb98fd7cf2acce72 Mon Sep 17 00:00:00 2001 From: bhavik-m <bhavikmehta2001@gmail.com> Date: Tue, 11 Jan 2022 17:12:20 +0530 Subject: [PATCH 8/8] fixed getScanApikey func --- hardhat.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 8c10d28f..44718268 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -118,7 +118,7 @@ const config: HardhatUserConfig = { tests: "./test", }, etherscan: { - apiKey: getScanApiKey(getNetworkUrl(String(process.env.networkType))), + apiKey: getScanApiKey(String(process.env.networkType)), }, typechain: { outDir: "typechain",