diff --git a/contracts/Erc20PiptSwap.sol b/contracts/Erc20PiptSwap.sol index a18d81ea..65c3bd01 100644 --- a/contracts/Erc20PiptSwap.sol +++ b/contracts/Erc20PiptSwap.sol @@ -34,11 +34,12 @@ contract Erc20PiptSwap is EthPiptSwap { constructor( address _weth, + address _usdc, address _cvp, address _pipt, address _piptWrapper, address _feeManager - ) public EthPiptSwap(_weth, _cvp, _pipt, _piptWrapper, _feeManager) {} + ) public EthPiptSwap(_weth, _usdc, _cvp, _pipt, _piptWrapper, _feeManager) {} function swapErc20ToPipt( address _swapToken, @@ -89,7 +90,8 @@ contract Erc20PiptSwap is EthPiptSwap { uint256 poolOut ) { - uint256 ethAmount = getAmountOutForUniswapValue(_uniswapPairFor(_swapToken), _swapAmount, true); + uint256 ethAmount = + getAmountOutForUniswapValue(_uniswapPairFor(address(weth), _swapToken), address(weth), _swapAmount, true); if (_withFee) { (, ethAmount) = calcEthFee(ethAmount, getWrapFee(_tokens)); @@ -104,12 +106,13 @@ contract Erc20PiptSwap is EthPiptSwap { ) external view returns (uint256) { uint256 resultEth = calcNeedEthToPoolOut(_poolAmountOut, _slippage); - IUniswapV2Pair tokenPair = _uniswapPairFor(_swapToken); + IUniswapV2Pair tokenPair = _uniswapPairFor(address(weth), _swapToken); + uint256 fee = getPairSwapFee(tokenPair); (uint256 token1Reserve, uint256 token2Reserve, ) = tokenPair.getReserves(); if (tokenPair.token0() == address(weth)) { - return UniswapV2Library.getAmountIn(resultEth.mul(1003).div(1000), token2Reserve, token1Reserve); + return UniswapV2Library.getAmountIn(resultEth.mul(1e4 + fee).div(1e4), token2Reserve, token1Reserve); } else { - return UniswapV2Library.getAmountIn(resultEth.mul(1003).div(1000), token1Reserve, token2Reserve); + return UniswapV2Library.getAmountIn(resultEth.mul(1e4 + fee).div(1e4), token1Reserve, token2Reserve); } } @@ -124,17 +127,27 @@ contract Erc20PiptSwap is EthPiptSwap { returns ( uint256[] memory tokensOutPipt, uint256[] memory ethOutUniswap, + uint256[] memory usdcOutUniswap, uint256 totalErc20Out, uint256 poolAmountFee ) { uint256 totalEthOut; - (tokensOutPipt, ethOutUniswap, totalEthOut, poolAmountFee) = calcSwapPiptToEthInputs(_poolAmountIn, _tokens); + (tokensOutPipt, ethOutUniswap, usdcOutUniswap, totalEthOut, poolAmountFee) = calcSwapPiptToEthInputs( + _poolAmountIn, + _tokens + ); + if (_withFee) { (, totalEthOut) = calcEthFee(totalEthOut, getWrapFee(_tokens)); } - totalErc20Out = getAmountOutForUniswapValue(_uniswapPairFor(_swapToken), totalEthOut, false); + totalErc20Out = getAmountOutForUniswapValue( + _uniswapPairFor(address(weth), _swapToken), + address(weth), + totalEthOut, + false + ); } function calcErc20Fee(address _swapToken, uint256 _swapAmount) @@ -147,15 +160,15 @@ contract Erc20PiptSwap is EthPiptSwap { uint256 ethAfterFee ) { - IUniswapV2Pair tokenPair = _uniswapPairFor(_swapToken); + IUniswapV2Pair tokenPair = _uniswapPairFor(address(weth), _swapToken); - uint256 ethAmount = getAmountOutForUniswapValue(tokenPair, _swapAmount, true); + uint256 ethAmount = getAmountOutForUniswapValue(tokenPair, address(weth), _swapAmount, true); (ethFee, ethAfterFee) = calcEthFee(ethAmount, getWrapFee(getPiptTokens())); if (ethFee != 0) { - erc20Fee = getAmountOutForUniswapValue(tokenPair, ethFee, false); + erc20Fee = getAmountOutForUniswapValue(tokenPair, address(weth), ethFee, false); } - erc20AfterFee = getAmountOutForUniswapValue(tokenPair, ethAfterFee, false); + erc20AfterFee = getAmountOutForUniswapValue(tokenPair, address(weth), ethAfterFee, false); } } diff --git a/contracts/EthPiptSwap.sol b/contracts/EthPiptSwap.sol index 53051601..2c2f0516 100644 --- a/contracts/EthPiptSwap.sol +++ b/contracts/EthPiptSwap.sol @@ -22,18 +22,22 @@ contract EthPiptSwap is ProgressiveFee { using SafeERC20 for BPoolInterface; TokenInterface public weth; + TokenInterface public usdc; TokenInterface public cvp; BPoolInterface public pipt; PowerIndexWrapperInterface public piptWrapper; - mapping(address => address) public uniswapEthPairByTokenAddress; - mapping(address => address) public uniswapEthPairToken0; + mapping(address => mapping(address => address)) public uniswapPairByTargetAndTokenAddress; + mapping(address => address) public uniswapPairToken0; mapping(address => bool) public reApproveTokens; + mapping(address => bool) public simplePairs; + mapping(address => uint256) public customPairsFee; uint256 public defaultSlippage; uint256 public defaultDiffPercent; struct CalculationStruct { uint256 tokenAmount; + uint256 usdAmount; uint256 ethAmount; uint256 tokenReserve; uint256 ethReserve; @@ -42,6 +46,8 @@ contract EthPiptSwap is ProgressiveFee { event SetTokenSetting(address indexed token, bool indexed reApprove, address indexed uniswapPair); event SetDefaultSlippage(uint256 newDefaultSlippage); event SetPiptWrapper(address _piptWrapper); + event SetSimplePairs(address indexed pair, bool indexed isSimple); + event SetCustomPairFee(address indexed pair, uint256 isSimple); event EthToPiptSwap( address indexed user, @@ -62,6 +68,7 @@ contract EthPiptSwap is ProgressiveFee { constructor( address _weth, + address _usdc, address _cvp, address _pipt, address _piptWrapper, @@ -69,6 +76,7 @@ contract EthPiptSwap is ProgressiveFee { ) public { __Ownable_init(); weth = TokenInterface(_weth); + usdc = TokenInterface(_usdc); cvp = TokenInterface(_cvp); pipt = BPoolInterface(_pipt); piptWrapper = PowerIndexWrapperInterface(_piptWrapper); @@ -106,6 +114,7 @@ contract EthPiptSwap is ProgressiveFee { function getMaxDiffPercent(uint256[] memory _ethInUniswap) public view returns (uint256 maxDiffPercent) { uint256 len = _ethInUniswap.length; uint256 minEthInUniswap = _ethInUniswap[0]; + for (uint256 i = 1; i < len; i++) { if (_ethInUniswap[i] < minEthInUniswap) { minEthInUniswap = _ethInUniswap[i]; @@ -138,13 +147,13 @@ contract EthPiptSwap is ProgressiveFee { Address.sendValue(msg.sender, ethOutAmount); } - function convertOddToCvpAndSendToPayout(address[] memory oddTokens) external { + function convertOddToCvpAndSendToPayout(address[] memory _oddTokens) external { require(msg.sender == tx.origin && !Address.isContract(msg.sender), "CONTRACT_NOT_ALLOWED"); - uint256 len = oddTokens.length; + uint256 len = _oddTokens.length; for (uint256 i = 0; i < len; i++) { - _swapTokenForWethOut(oddTokens[i], TokenInterface(oddTokens[i]).balanceOf(address(this))); + _swapTokenForWethOut(_oddTokens[i], TokenInterface(_oddTokens[i]).balanceOf(address(this))); } uint256 wethBalance = weth.balanceOf(address(this)); @@ -158,21 +167,49 @@ contract EthPiptSwap is ProgressiveFee { function setTokensSettings( address[] memory _tokens, address[] memory _pairs, - bool[] memory _reapprove + address _pairTargetToken, + bool[] memory _reapprove, + uint256 _customPairFee ) external onlyOwner { uint256 len = _tokens.length; require(len == _pairs.length && len == _reapprove.length, "LENGTHS_NOT_EQUAL"); for (uint256 i = 0; i < len; i++) { - _setUniswapSettingAndPrepareToken(_tokens[i], _pairs[i]); + _setUniswapSettingAndPrepareToken(_pairTargetToken, _tokens[i], _pairs[i], _customPairFee); reApproveTokens[_tokens[i]] = _reapprove[i]; emit SetTokenSetting(_tokens[i], _reapprove[i], _pairs[i]); } } - function fetchUnswapPairsFromFactory(address _factory, address[] calldata _tokens) external onlyOwner { + function setSimplePairs(address[] memory _pairs, bool _isSimple) external onlyOwner { + uint256 len = _pairs.length; + for (uint256 i = 0; i < len; i++) { + simplePairs[_pairs[i]] = _isSimple; + emit SetSimplePairs(_pairs[i], _isSimple); + } + } + + function setPairsCustomFee(address[] memory _pairs, uint256 _fee) external onlyOwner { + uint256 len = _pairs.length; + for (uint256 i = 0; i < len; i++) { + customPairsFee[_pairs[i]] = _fee; + emit SetCustomPairFee(_pairs[i], _fee); + } + } + + function fetchUnswapPairsFromFactory( + address _factory, + address _targetToken, + address[] calldata _tokens, + uint256 _customPairFee + ) external onlyOwner { uint256 len = _tokens.length; for (uint256 i = 0; i < len; i++) { - _setUniswapSettingAndPrepareToken(_tokens[i], IUniswapV2Factory(_factory).getPair(_tokens[i], address(weth))); + _setUniswapSettingAndPrepareToken( + _targetToken, + _tokens[i], + IUniswapV2Factory(_factory).getPair(_tokens[i], _targetToken), + _customPairFee + ); } } @@ -216,11 +253,34 @@ contract EthPiptSwap is ProgressiveFee { for (uint256 i = 0; i < _tokens.length; i++) { // token share relatively 1 ether of first token calculations[i].tokenAmount = poolRatio.mul(getPiptTokenBalance(_tokens[i])).div(1 ether); - calculations[i].ethAmount = getAmountInForUniswapValue( - _uniswapPairFor(_tokens[i]), - calculations[i].tokenAmount, - true - ); + + address wethAddress = address(weth); + address usdcAddress = address(usdc); + address wethPairAddress = uniswapPairByTargetAndTokenAddress[wethAddress][_tokens[i]]; + + if (wethPairAddress == address(0)) { + address usdPairAddress = uniswapPairByTargetAndTokenAddress[usdcAddress][_tokens[i]]; + calculations[i].usdAmount = getAmountInForUniswapValue( + IUniswapV2Pair(usdPairAddress), + usdc, + calculations[i].tokenAmount, + true + ); + calculations[i].ethAmount = getAmountInForUniswapValue( + _uniswapPairFor(wethAddress, usdcAddress), + weth, + calculations[i].usdAmount, + true + ); + } else { + calculations[i].ethAmount = getAmountInForUniswapValue( + IUniswapV2Pair(wethPairAddress), + weth, + calculations[i].tokenAmount, + true + ); + } + totalEthRequired = totalEthRequired.add(calculations[i].ethAmount); } } @@ -242,12 +302,14 @@ contract EthPiptSwap is ProgressiveFee { returns ( uint256[] memory tokensOutPipt, uint256[] memory ethOutUniswap, + uint256[] memory usdOutUniswap, uint256 totalEthOut, uint256 poolAmountFee ) { tokensOutPipt = new uint256[](_tokens.length); ethOutUniswap = new uint256[](_tokens.length); + usdOutUniswap = new uint256[](_tokens.length); (, , uint256 communityExitFee, ) = pipt.getCommunityFee(); @@ -263,7 +325,32 @@ contract EthPiptSwap is ProgressiveFee { totalEthOut = 0; for (uint256 i = 0; i < _tokens.length; i++) { tokensOutPipt[i] = poolRatio.mul(getPiptTokenBalance(_tokens[i])).div(1 ether); - ethOutUniswap[i] = getAmountOutForUniswapValue(_uniswapPairFor(_tokens[i]), tokensOutPipt[i], true); + address wethAddress = address(weth); + address usdcAddress = address(usdc); + address wethPairAddress = uniswapPairByTargetAndTokenAddress[wethAddress][_tokens[i]]; + + if (wethPairAddress == address(0)) { + address usdPairAddress = uniswapPairByTargetAndTokenAddress[usdcAddress][_tokens[i]]; + usdOutUniswap[i] = getAmountOutForUniswapValue( + IUniswapV2Pair(usdPairAddress), + usdcAddress, + tokensOutPipt[i], + true + ); + ethOutUniswap[i] = getAmountOutForUniswapValue( + _uniswapPairFor(wethAddress, usdcAddress), + wethAddress, + usdOutUniswap[i], + true + ); + } else { + ethOutUniswap[i] = getAmountOutForUniswapValue( + IUniswapV2Pair(wethPairAddress), + wethAddress, + tokensOutPipt[i], + true + ); + } totalEthOut = totalEthOut.add(ethOutUniswap[i]); } } @@ -274,28 +361,53 @@ contract EthPiptSwap is ProgressiveFee { address[] memory tokens = getPiptTokens(); uint256 len = tokens.length; - CalculationStruct[] memory calculations = new CalculationStruct[](len); uint256[] memory tokensInPipt = new uint256[](len); uint256 totalEthSwap = 0; for (uint256 i = 0; i < len; i++) { tokensInPipt[i] = ratio.mul(getPiptTokenBalance(tokens[i])).div(1 ether); - totalEthSwap = getAmountInForUniswapValue(_uniswapPairFor(tokens[i]), tokensInPipt[i], true).add(totalEthSwap); + + address wethAddress = address(weth); + address usdcAddress = address(usdc); + address wethPairAddress = uniswapPairByTargetAndTokenAddress[wethAddress][tokens[i]]; + + if (wethPairAddress == address(0)) { + address usdPairAddress = uniswapPairByTargetAndTokenAddress[usdcAddress][tokens[i]]; + uint256 usdAmount = getAmountInForUniswapValue(IUniswapV2Pair(usdPairAddress), usdc, tokensInPipt[i], true); + totalEthSwap = getAmountInForUniswapValue(_uniswapPairFor(wethAddress, usdcAddress), weth, usdAmount, true).add( + totalEthSwap + ); + } else { + totalEthSwap = getAmountInForUniswapValue(IUniswapV2Pair(wethPairAddress), weth, tokensInPipt[i], true).add( + totalEthSwap + ); + } } return totalEthSwap.add(totalEthSwap.mul(_slippage).div(1 ether)); } - function calcEthFee(uint256 ethAmount, uint256 wrapperFee) public view returns (uint256 ethFee, uint256 ethAfterFee) { - return calcFee(ethAmount, wrapperFee); + function calcEthFee(uint256 _ethAmount, uint256 _wrapperFee) + public + view + returns (uint256 ethFee, uint256 ethAfterFee) + { + return calcFee(_ethAmount, _wrapperFee); } - function calcEthFee(uint256 ethAmount) external view returns (uint256 ethFee, uint256 ethAfterFee) { - (ethFee, ethAfterFee) = calcEthFee(ethAmount, getWrapFee(getPiptTokens())); + function calcEthFee(uint256 _ethAmount) external view returns (uint256 ethFee, uint256 ethAfterFee) { + (ethFee, ethAfterFee) = calcEthFee(_ethAmount, getWrapFee(getPiptTokens())); } - function getWrapFee(address[] memory tokens) public view returns (uint256 wrapperFee) { + function getWrapFee(address[] memory _tokens) public view returns (uint256 wrapperFee) { if (address(piptWrapper) != address(0)) { - wrapperFee = piptWrapper.calcEthFeeForTokens(tokens); + wrapperFee = piptWrapper.calcEthFeeForTokens(_tokens); + } + } + + function getPairSwapFee(IUniswapV2Pair _tokenPair) public view returns (uint256 fee) { + fee = customPairsFee[address(_tokenPair)]; + if (fee == 0) { + fee = 30; } } @@ -307,12 +419,20 @@ contract EthPiptSwap is ProgressiveFee { return address(piptWrapper) == address(0) ? pipt.getBalance(_token) : piptWrapper.getBalance(_token); } + function getTokenPairs(address _token) external view returns (address wethPair, address usdPair) { + return ( + uniswapPairByTargetAndTokenAddress[address(weth)][_token], + uniswapPairByTargetAndTokenAddress[address(usdc)][_token] + ); + } + function getAmountInForUniswap( IUniswapV2Pair _tokenPair, + IERC20 _targetToken, uint256 _swapAmount, bool _isEthIn ) public view returns (uint256 amountIn, bool isInverse) { - isInverse = uniswapEthPairToken0[address(_tokenPair)] == address(weth); + isInverse = uniswapPairToken0[address(_tokenPair)] == address(_targetToken); if (_isEthIn ? !isInverse : isInverse) { (uint256 ethReserve, uint256 tokenReserve, ) = _tokenPair.getReserves(); amountIn = UniswapV2Library.getAmountIn(_swapAmount, tokenReserve, ethReserve); @@ -324,49 +444,61 @@ contract EthPiptSwap is ProgressiveFee { function getAmountInForUniswapValue( IUniswapV2Pair _tokenPair, + IERC20 _targetToken, uint256 _swapAmount, bool _isEthIn ) public view returns (uint256 amountIn) { - (amountIn, ) = getAmountInForUniswap(_tokenPair, _swapAmount, _isEthIn); + (amountIn, ) = getAmountInForUniswap(_tokenPair, _targetToken, _swapAmount, _isEthIn); } function getAmountOutForUniswap( IUniswapV2Pair _tokenPair, + address _targetToken, uint256 _swapAmount, bool _isEthOut ) public view returns (uint256 amountOut, bool isInverse) { - isInverse = uniswapEthPairToken0[address(_tokenPair)] == address(weth); + isInverse = uniswapPairToken0[address(_tokenPair)] == _targetToken; + uint256 fee = getPairSwapFee(_tokenPair); if (_isEthOut ? isInverse : !isInverse) { (uint256 ethReserve, uint256 tokenReserve, ) = _tokenPair.getReserves(); - amountOut = UniswapV2Library.getAmountOut(_swapAmount, tokenReserve, ethReserve); + amountOut = UniswapV2Library.getAmountOut(_swapAmount, tokenReserve, ethReserve, fee); } else { (uint256 tokenReserve, uint256 ethReserve, ) = _tokenPair.getReserves(); - amountOut = UniswapV2Library.getAmountOut(_swapAmount, tokenReserve, ethReserve); + amountOut = UniswapV2Library.getAmountOut(_swapAmount, tokenReserve, ethReserve, fee); } } function getAmountOutForUniswapValue( IUniswapV2Pair _tokenPair, + address _targetToken, uint256 _swapAmount, bool _isEthOut ) public view returns (uint256 ethAmount) { - (ethAmount, ) = getAmountOutForUniswap(_tokenPair, _swapAmount, _isEthOut); + (ethAmount, ) = getAmountOutForUniswap(_tokenPair, _targetToken, _swapAmount, _isEthOut); } - function _setUniswapSettingAndPrepareToken(address _token, address _pair) internal { - uniswapEthPairByTokenAddress[_token] = _pair; - uniswapEthPairToken0[_pair] = IUniswapV2Pair(_pair).token0(); + function _setUniswapSettingAndPrepareToken( + address _targetToken, + address _token, + address _pair, + uint256 _customPairFee + ) internal { + uniswapPairByTargetAndTokenAddress[_targetToken][_token] = _pair; + uniswapPairToken0[_pair] = IUniswapV2Pair(_pair).token0(); + if (customPairsFee[_pair] != _customPairFee) { + customPairsFee[_pair] = _customPairFee; + } } - function _uniswapPairFor(address token) internal view returns (IUniswapV2Pair) { - return IUniswapV2Pair(uniswapEthPairByTokenAddress[token]); + function _uniswapPairFor(address _targetToken, address _token) internal view returns (IUniswapV2Pair) { + return IUniswapV2Pair(uniswapPairByTargetAndTokenAddress[_targetToken][_token]); } function _swapWethToPiptByPoolOut( uint256 _wethAmount, uint256 _poolAmountOut, - address[] memory tokens, - uint256 wrapperFee + address[] memory _tokens, + uint256 _wrapperFee ) internal returns (uint256 poolAmountOutAfterFee, uint256 oddEth) { require(_wethAmount > 0, "ETH_REQUIRED"); @@ -378,8 +510,8 @@ contract EthPiptSwap is ProgressiveFee { } } - (uint256 feeAmount, uint256 swapAmount) = calcEthFee(_wethAmount, wrapperFee); - (uint256[] memory tokensInPipt, uint256 totalEthSwap) = _prepareTokensForJoin(tokens, _poolAmountOut); + (uint256 feeAmount, uint256 swapAmount) = calcEthFee(_wethAmount, _wrapperFee); + (uint256[] memory tokensInPipt, uint256 totalEthSwap) = _prepareTokensForJoin(_tokens, _poolAmountOut); { uint256 poolAmountOutFee; @@ -393,8 +525,8 @@ contract EthPiptSwap is ProgressiveFee { emit EthToPiptSwap(msg.sender, swapAmount, feeAmount, _poolAmountOut, poolAmountOutFee); } - _joinPool(_poolAmountOut, tokensInPipt, wrapperFee); - totalEthSwap = totalEthSwap.add(wrapperFee); + _joinPool(_poolAmountOut, tokensInPipt, _wrapperFee); + totalEthSwap = totalEthSwap.add(_wrapperFee); pipt.safeTransfer(msg.sender, poolAmountOutAfterFee); oddEth = swapAmount.sub(totalEthSwap); @@ -414,7 +546,21 @@ contract EthPiptSwap is ProgressiveFee { uint256 ratio = _poolAmountOut.mul(1 ether).div(pipt.totalSupply()).add(100); for (uint256 i = 0; i < len; i++) { tokensInPipt[i] = ratio.mul(getPiptTokenBalance(_tokens[i])).div(1 ether); - totalEthSwap = totalEthSwap.add(_swapWethForTokenIn(_tokens[i], tokensInPipt[i])); + + address wethAddress = address(weth); + address usdcAddress = address(usdc); + address wethPairAddress = uniswapPairByTargetAndTokenAddress[wethAddress][_tokens[i]]; + + if (wethPairAddress == address(0)) { + address usdPairAddress = uniswapPairByTargetAndTokenAddress[usdcAddress][_tokens[i]]; + (uint256 usdAmountIn, ) = getAmountInForUniswap(IUniswapV2Pair(usdPairAddress), usdc, tokensInPipt[i], true); + + uint256 ethAmountIn = _swapForTokenIn(weth, address(usdc), usdAmountIn); + _swapForTokenIn(usdc, _tokens[i], tokensInPipt[i]); + totalEthSwap = totalEthSwap.add(ethAmountIn); + } else { + totalEthSwap = totalEthSwap.add(_swapForTokenIn(weth, _tokens[i], tokensInPipt[i])); + } address approveAddress = address(piptWrapper) == address(0) ? address(pipt) : address(piptWrapper); if (reApproveTokens[_tokens[i]]) { @@ -428,23 +574,59 @@ contract EthPiptSwap is ProgressiveFee { address[] memory tokens = getPiptTokens(); uint256 len = tokens.length; - (uint256[] memory tokensOutPipt, uint256[] memory ethOutUniswap, uint256 totalEthOut, uint256 poolAmountFee) = - calcSwapPiptToEthInputs(_poolAmountIn, tokens); + ( + uint256[] memory tokensOutPipt, + uint256[] memory ethOutUniswap, + uint256[] memory usdOutUniswap, + uint256 totalEthOut, + uint256 poolAmountFee + ) = calcSwapPiptToEthInputs(_poolAmountIn, tokens); pipt.safeTransferFrom(msg.sender, address(this), _poolAmountIn); uint256 wrapperFee = getWrapFee(tokens); - (uint256 ethFeeAmount, uint256 ethOutAmount) = calcEthFee(totalEthOut, wrapperFee); - _exitPool(_poolAmountIn, tokensOutPipt, wrapperFee); + totalEthOut = 0; + for (uint256 i = 0; i < len; i++) { - IUniswapV2Pair tokenPair = _uniswapPairFor(tokens[i]); - TokenInterface(tokens[i]).safeTransfer(address(tokenPair), tokensOutPipt[i]); - tokenPair.swap(uint256(0), ethOutUniswap[i], address(this), new bytes(0)); + if (usdOutUniswap[i] == 0) { + IUniswapV2Pair wethPair = _uniswapPairFor(address(weth), tokens[i]); + _swapToken( + tokens[i], + wethPair, + tokensOutPipt[i], + ethOutUniswap[i], + uniswapPairToken0[address(wethPair)] == address(weth) + ); + totalEthOut = totalEthOut.add(ethOutUniswap[i]); + } else { + IUniswapV2Pair usdcPair = _uniswapPairFor(address(usdc), tokens[i]); + _swapToken( + tokens[i], + usdcPair, + tokensOutPipt[i], + usdOutUniswap[i], + uniswapPairToken0[address(usdcPair)] == address(usdc) + ); + + IUniswapV2Pair wethPair = _uniswapPairFor(address(weth), address(usdc)); + ethOutUniswap[i] = getAmountOutForUniswapValue(wethPair, address(weth), usdOutUniswap[i], true); + _swapToken( + address(usdc), + wethPair, + usdOutUniswap[i], + ethOutUniswap[i], + uniswapPairToken0[address(wethPair)] == address(weth) + ); + + totalEthOut = totalEthOut.add(ethOutUniswap[i]); + } } + (uint256 ethFeeAmount, uint256 ethOutAmount) = calcEthFee(totalEthOut, wrapperFee); + emit PiptToEthSwap(msg.sender, _poolAmountIn, poolAmountFee, ethOutAmount, ethFeeAmount); return ethOutAmount; @@ -479,27 +661,45 @@ contract EthPiptSwap is ProgressiveFee { } } - function _swapWethForTokenIn(address _erc20, uint256 _erc20Out) internal returns (uint256 ethIn) { - IUniswapV2Pair tokenPair = _uniswapPairFor(_erc20); + function _swapForTokenIn( + IERC20 _tokenIn, + address _erc20, + uint256 _erc20Out + ) internal returns (uint256 amountIn) { + IUniswapV2Pair tokenPair = _uniswapPairFor(address(_tokenIn), _erc20); bool isInverse; - (ethIn, isInverse) = getAmountInForUniswap(tokenPair, _erc20Out, true); - weth.safeTransfer(address(tokenPair), ethIn); - tokenPair.swap(isInverse ? uint256(0) : _erc20Out, isInverse ? _erc20Out : uint256(0), address(this), new bytes(0)); + (amountIn, isInverse) = getAmountInForUniswap(tokenPair, _tokenIn, _erc20Out, true); + _swapToken(address(_tokenIn), tokenPair, amountIn, _erc20Out, !isInverse); } function _swapWethForTokenOut(address _erc20, uint256 _ethIn) internal returns (uint256 erc20Out) { - IUniswapV2Pair tokenPair = _uniswapPairFor(_erc20); + IUniswapV2Pair tokenPair = _uniswapPairFor(address(weth), _erc20); bool isInverse; - (erc20Out, isInverse) = getAmountOutForUniswap(tokenPair, _ethIn, false); - weth.safeTransfer(address(tokenPair), _ethIn); - tokenPair.swap(isInverse ? uint256(0) : erc20Out, isInverse ? erc20Out : uint256(0), address(this), new bytes(0)); + (erc20Out, isInverse) = getAmountOutForUniswap(tokenPair, address(weth), _ethIn, false); + _swapToken(address(weth), tokenPair, _ethIn, erc20Out, !isInverse); } function _swapTokenForWethOut(address _erc20, uint256 _erc20In) internal returns (uint256 ethOut) { - IUniswapV2Pair tokenPair = _uniswapPairFor(_erc20); + IUniswapV2Pair tokenPair = _uniswapPairFor(address(weth), _erc20); bool isInverse; - (ethOut, isInverse) = getAmountOutForUniswap(tokenPair, _erc20In, true); - IERC20(_erc20).safeTransfer(address(tokenPair), _erc20In); - tokenPair.swap(isInverse ? ethOut : uint256(0), isInverse ? uint256(0) : ethOut, address(this), new bytes(0)); + (ethOut, isInverse) = getAmountOutForUniswap(tokenPair, address(weth), _erc20In, true); + _swapToken(_erc20, tokenPair, _erc20In, ethOut, isInverse); + } + + function _swapToken( + address _token, + IUniswapV2Pair _tokenPair, + uint256 _amountIn, + uint256 _amountOut, + bool _isInverse + ) internal { + IERC20(_token).safeTransfer(address(_tokenPair), _amountIn); + uint256 amount0 = _isInverse ? _amountOut : uint256(0); + uint256 amount1 = _isInverse ? uint256(0) : _amountOut; + if (simplePairs[address(_tokenPair)]) { + _tokenPair.swap(amount0, amount1, address(this)); + } else { + _tokenPair.swap(amount0, amount1, address(this), new bytes(0)); + } } } diff --git a/contracts/interfaces/IUniswapV2Pair.sol b/contracts/interfaces/IUniswapV2Pair.sol index 2019e43c..a8c77a35 100644 --- a/contracts/interfaces/IUniswapV2Pair.sol +++ b/contracts/interfaces/IUniswapV2Pair.sol @@ -83,6 +83,12 @@ interface IUniswapV2Pair { function burn(address to) external returns (uint256 amount0, uint256 amount1); + function swap( + uint256 amount0Out, + uint256 amount1Out, + address to + ) external; + function swap( uint256 amount0Out, uint256 amount1Out, diff --git a/contracts/lib/UniswapV2Library.sol b/contracts/lib/UniswapV2Library.sol index de02f611..bd8cb26e 100644 --- a/contracts/lib/UniswapV2Library.sol +++ b/contracts/lib/UniswapV2Library.sol @@ -40,20 +40,30 @@ library UniswapV2Library { // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) { + return getAmountOut(amountIn, reserveIn, reserveOut, 30); + } + + // given an output amount of an asset and pair reserves, returns a required input amount of the other asset + function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) { + return getAmountIn(amountOut, reserveIn, reserveOut, 30); + } + + // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset + function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut, uint fee) internal pure returns (uint amountOut) { require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT'); require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); - uint amountInWithFee = amountIn.mul(997); + uint amountInWithFee = amountIn.mul(1e4 - fee); uint numerator = amountInWithFee.mul(reserveOut); - uint denominator = reserveIn.mul(1000).add(amountInWithFee); + uint denominator = reserveIn.mul(1e4).add(amountInWithFee); amountOut = numerator / denominator; } // given an output amount of an asset and pair reserves, returns a required input amount of the other asset - function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) { + function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut, uint fee) internal pure returns (uint amountIn) { require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT'); require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY'); - uint numerator = reserveIn.mul(amountOut).mul(1000); - uint denominator = reserveOut.sub(amountOut).mul(997); + uint numerator = reserveIn.mul(amountOut).mul(1e4); + uint denominator = reserveOut.sub(amountOut).mul(1e4 - fee); amountIn = (numerator / denominator).add(1); } diff --git a/data/poolsData.json b/data/poolsData.json index ea440785..36df32f2 100644 --- a/data/poolsData.json +++ b/data/poolsData.json @@ -130,14 +130,14 @@ { "tokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "tokenSymbol": "USDC", - "tokenDecimals": "6", - "totalSupply": "5859506145609550", + "tokenDecimals": "18", + "totalSupply": "5859506145609550000000000000", "balancerBalance": "0", "excludeBalances": [], "oraclePrice": "1000000000000000000000000000000", "uniswapPair": { "address": "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc", - "tokenReserve": "147035058180672", + "tokenReserve": "147035058180672000000000000", "ethReserve": "109340791029131893908074", "isReverse": false } @@ -187,4 +187,4 @@ "isReverse": false } } -] \ No newline at end of file +] diff --git a/test/EthPiptSwap.test.js b/test/EthPiptSwap.test.js index 56ab9b69..9ec80a7a 100644 --- a/test/EthPiptSwap.test.js +++ b/test/EthPiptSwap.test.js @@ -33,7 +33,7 @@ PowerIndexWrapper.numberFormat = 'String'; const { web3 } = PowerIndexPoolFactory; -const { ether, getTimestamp, subBN, addBN, assertEqualWithAccuracy, isBNHigher } = require('./helpers'); +const { ether, mwei, getTimestamp, subBN, addBN, mulBN, assertEqualWithAccuracy, isBNHigher } = require('./helpers'); describe('EthPiptSwap and Erc20PiptSwap', () => { const zeroAddress = '0x0000000000000000000000000000000000000000'; @@ -78,14 +78,13 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { ); }; - this.makePowerIndexPool = async (_tokens, _balances) => { + this.makePowerIndexPool = async (_tokens, _balances, _weights) => { const fromTimestamp = await getTimestamp(100); const targetTimestamp = await getTimestamp(100 + 60 * 60 * 24 * 5); for (let i = 0; i < _tokens.length; i++) { await _tokens[i].approve(this.bActions.address, '0x' + 'f'.repeat(64)); } - const weightPart = 50 / _tokens.length; const minWeightPerSecond = ether('0.00000001'); const maxWeightPerSecond = ether('0.1'); @@ -106,7 +105,7 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { _tokens.map((t, i) => ({ token: t.address, balance: _balances[i], - targetDenorm: ether(weightPart), + targetDenorm: ether(_weights[i]), fromTimestamp: fromTimestamp.toString(), targetTimestamp: targetTimestamp.toString() })), @@ -119,20 +118,20 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { return pool; }; - this.makeUniswapPair = async (_token, _tokenBalance, _wethBalance, isReverse) => { - const token0 = isReverse ? this.weth.address : _token.address; - const token1 = isReverse ? _token.address : this.weth.address; + this.makeUniswapPair = async (_token, targetToken, _tokenBalance, _targetBalance, isReverse) => { + const token0 = isReverse ? targetToken.address : _token.address; + const token1 = isReverse ? _token.address : targetToken.address; const res = await this.uniswapFactory.createPairMock(token0, token1); const pair = await UniswapV2Pair.at(res.logs[0].args.pair); await _token.transfer(pair.address, _tokenBalance); - await this.weth.transfer(pair.address, _wethBalance); + await targetToken.transfer(pair.address, _targetBalance); await pair.mint(minter); return pair; }; }); describe('Swaps with Uniswap mainnet values', () => { - let cvp, cvpPair, tokens, balancerTokens, pairs, bPoolBalances, pool; + let cvp, usdc, cvpPair, tokens, balancerTokens, pairs, bPoolBalances, pool; const tokenBySymbol = {}; @@ -146,7 +145,7 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { const token = await MockERC20.new(poolsData[i].tokenSymbol, poolsData[i].tokenSymbol, poolsData[i].tokenDecimals, ether('10000000000')); const uniPair = poolsData[i].uniswapPair; - const pair = await this.makeUniswapPair(token, uniPair.tokenReserve, uniPair.ethReserve, uniPair.isReverse); + const pair = await this.makeUniswapPair(token, this.weth, uniPair.tokenReserve, uniPair.ethReserve, uniPair.isReverse); tokens.push(token); pairs.push(pair); bPoolBalances.push(poolsData[i].balancerBalance); @@ -154,19 +153,22 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { cvp = token; cvpPair = pair; } + if (poolsData[i].tokenSymbol === 'USDC') { + usdc = token; + } tokenBySymbol[poolsData[i].tokenSymbol] = { token, pair }; } balancerTokens = tokens.filter((t, i) => poolsData[i].balancerBalance !== '0'); - pool = await this.makePowerIndexPool(balancerTokens, bPoolBalances.filter(b => b !== '0')); + pool = await this.makePowerIndexPool(balancerTokens, bPoolBalances.filter(b => b !== '0'), balancerTokens.map(() => 50 / balancerTokens.length)); await time.increase(12 * 60 * 60); }); it('diff percent check should work properly', async () => { - const ethPiptSwap = await EthPiptSwap.new(this.weth.address, cvp.address, pool.address, zeroAddress, feeManager, { + const ethPiptSwap = await EthPiptSwap.new(this.weth.address, usdc.address, cvp.address, pool.address, zeroAddress, feeManager, { from: minter, }); @@ -175,7 +177,9 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { await ethPiptSwap.setTokensSettings( tokens.map(t => t.address), pairs.map(p => p.address), + this.weth.address, pairs.map(() => true), + '0', {from: minter}, ); @@ -210,7 +214,7 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { }); it('swapEthToPipt should work properly', async () => { - const ethPiptSwap = await EthPiptSwap.new(this.weth.address, cvp.address, pool.address, zeroAddress, feeManager, { + const ethPiptSwap = await EthPiptSwap.new(this.weth.address, usdc.address, cvp.address, pool.address, zeroAddress, feeManager, { from: minter, }); @@ -225,7 +229,9 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { ethPiptSwap.setTokensSettings( tokens.map(t => t.address), pairs.map(p => p.address), + this.weth.address, pairs.map(() => true), + '0', { from: bob }, ), 'Ownable: caller is not the owner', @@ -234,7 +240,9 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { await ethPiptSwap.setTokensSettings( tokens.map(t => t.address), pairs.map(p => p.address), + this.weth.address, pairs.map(() => true), + '0', { from: minter }, ); @@ -362,7 +370,7 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { beforeEach(async () => { await this.poolRestrictions.setTotalRestrictions([pool.address], [ether('200').toString(10)], { from: minter }); - erc20PiptSwap = await Erc20PiptSwap.new(this.weth.address, cvp.address, pool.address, zeroAddress, feeManager, { + erc20PiptSwap = await Erc20PiptSwap.new(this.weth.address, usdc.address, cvp.address, pool.address, zeroAddress, feeManager, { from: minter, }); @@ -372,13 +380,17 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { await expectRevert(erc20PiptSwap.fetchUnswapPairsFromFactory( this.uniswapFactory.address, + this.weth.address, tokens.map(t => t.address), + '0', { from: bob }, ), 'Ownable: caller is not the owner'); await erc20PiptSwap.fetchUnswapPairsFromFactory( this.uniswapFactory.address, + this.weth.address, tokens.map(t => t.address), + '0', { from: minter }, ); }); @@ -489,13 +501,15 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { it('PowerIndexPool should prevent double swap in same transaction by EthPiptSwap', async () => { await this.poolRestrictions.setTotalRestrictions([pool.address], [ether('200').toString(10)], { from: minter }); - const ethPiptSwap = await EthPiptSwap.new(this.weth.address, cvp.address, pool.address, zeroAddress, feeManager, { + const ethPiptSwap = await EthPiptSwap.new(this.weth.address, usdc.address, cvp.address, pool.address, zeroAddress, feeManager, { from: minter, }); await ethPiptSwap.fetchUnswapPairsFromFactory( this.uniswapFactory.address, + this.weth.address, tokens.map(t => t.address), + '0', { from: minter }, ) @@ -527,6 +541,7 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { erc20PiptSwap = await Erc20PiptSwap.new( this.weth.address, + usdc.address, balancerTokens[1].address, pool.address, poolWrapper.address, @@ -536,7 +551,9 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { await erc20PiptSwap.fetchUnswapPairsFromFactory( this.uniswapFactory.address, + this.weth.address, tokens.map(t => t.address), + '0', {from: minter}, ); @@ -581,6 +598,13 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { }); it('swapEthToPipt should work properly', async () => { + for(let i = 0; i < tokens.length; i++) { + assert.equal( + await erc20PiptSwap.uniswapPairByTargetAndTokenAddress(this.weth.address, tokens[i].address), + await this.uniswapFactory.getPair(this.weth.address, tokens[i].address), + ); + } + const ethToSwap = ether('600').toString(10); const slippage = ether('0.05'); const maxDiff = ether('0.02'); @@ -646,7 +670,7 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { const {token: usdToken, pair: usdPair} = tokenBySymbol[erc20TokenSymbol]; const tokenAddress = usdToken.address; - const amountToSwap = (100 * 10 ** 6).toString(10); + const amountToSwap = (await usdToken.decimals() === '18') ? ether(100) : mwei(100); const slippage = ether('0.02'); const maxDiff = ether('0.04'); @@ -731,7 +755,7 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { }); describe('Swap with 20 tokens', () => { - let cvp, tokens, balancerTokens, pairs, bPoolBalances, pool; + let cvp, tokens, balancerTokens, pairs, bPoolBalances, pool, usdc; const tokenBySymbol = {}; @@ -741,31 +765,46 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { pairs = []; bPoolBalances = []; - const pools20Data = poolsData.concat(poolsData).concat(poolsData.slice(0, 4)); + const notBalancerPoolsData = poolsData.filter((t) => t.balancerBalance !== '0' && t.tokenSymbol !== 'CVP'); + const pools20Data = poolsData.concat(notBalancerPoolsData).concat(notBalancerPoolsData).slice(0, 20); + + let lastWeight; for (let i = 0; i < pools20Data.length; i++) { const token = await MockERC20.new(pools20Data[i].tokenSymbol, pools20Data[i].tokenSymbol, pools20Data[i].tokenDecimals, ether('10000000000')); const uniPair = pools20Data[i].uniswapPair; - const pair = await this.makeUniswapPair(token, uniPair.tokenReserve, uniPair.ethReserve, uniPair.isReverse); + let pair; + if (i === 19) { + pair = await this.makeUniswapPair(token, usdc, uniPair.tokenReserve, mulBN(uniPair.ethReserve, 1000), uniPair.isReverse); + } else { + pair = await this.makeUniswapPair(token, this.weth, uniPair.tokenReserve, uniPair.ethReserve, uniPair.isReverse); + } tokens.push(token); pairs.push(pair); - bPoolBalances.push(pools20Data[i].balancerBalance); if (pools20Data[i].tokenSymbol === 'CVP') { cvp = token; } + if (pools20Data[i].tokenSymbol === 'USDC') { + usdc = token; + lastWeight = (50 / 20) * 0.7436169615036432; + pools20Data[i].balancerBalance = mulBN(pools20Data[i].balancerBalance, 1000); + } + bPoolBalances.push(pools20Data[i].balancerBalance); tokenBySymbol[pools20Data[i].tokenSymbol] = { token, pair }; } balancerTokens = tokens.filter((t, i) => pools20Data[i].balancerBalance !== '0'); - pool = await this.makePowerIndexPool(balancerTokens, bPoolBalances.filter(b => b !== '0')); + const weights = tokens.map((t, i) => i === 19 ? lastWeight : (50 - lastWeight) / balancerTokens.length); + + pool = await this.makePowerIndexPool(balancerTokens, bPoolBalances.filter(b => b !== '0'), weights); await time.increase(12 * 60 * 60); }); it('swapEthToPipt should work properly', async () => { - const ethPiptSwap = await EthPiptSwap.new(this.weth.address, cvp.address, pool.address, zeroAddress, feeManager, { + const ethPiptSwap = await EthPiptSwap.new(this.weth.address, usdc.address, cvp.address, pool.address, zeroAddress, feeManager, { from: minter, }); @@ -774,15 +813,34 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { }); await ethPiptSwap.setTokensSettings( - tokens.map(t => t.address), - pairs.map(p => p.address), - pairs.map(() => true), + tokens.map(t => t.address).slice(0, 19), + pairs.map(p => p.address).slice(0, 19), + this.weth.address, + pairs.map(() => true).slice(0, 19), + '0', + {from: minter}, + ); + + await ethPiptSwap.setTokensSettings( + tokens.map(t => t.address).slice(19, 20), + pairs.map(p => p.address).slice(19, 20), + usdc.address, + pairs.map(() => true).slice(19, 20), + '0', {from: minter}, ); + for(let i = 0; i < tokens.length; i++) { + const targetTokenAddress = i === 19 ? usdc.address : this.weth.address; + assert.equal( + await ethPiptSwap.uniswapPairByTargetAndTokenAddress(targetTokenAddress, tokens[i].address), + await this.uniswapFactory.getPair(targetTokenAddress, tokens[i].address), + ); + } + const ethToSwap = ether('600').toString(10); const slippage = ether('0.05'); - const maxDiff = ether('0.02'); + const maxDiff = ether('100'); const {ethFee: ethInFee, ethAfterFee: ethInAfterFee} = await ethPiptSwap.calcEthFee(ethToSwap); @@ -820,6 +878,26 @@ describe('EthPiptSwap and Erc20PiptSwap', () => { assert.equal(swap.poolCommunityFee, poolOutFee); assert.equal(poolOutAfterFee, await pool.balanceOf(bob)); + + const swapPiptToEthInputs = await ethPiptSwap.calcSwapPiptToEthInputs( + poolOutAfterFee, + balancerTokens.map(t => t.address), + ); + + const { ethAfterFee: ethOutAfterFee } = await ethPiptSwap.calcEthFee( + swapPiptToEthInputs.totalEthOut, + ); + + await pool.approve(ethPiptSwap.address, poolOutAfterFee, { from: bob }); + + bobBalanceBefore = await web3.eth.getBalance(bob); + res = await ethPiptSwap.swapPiptToEth(poolOutAfterFee, { from: bob, gasPrice }); + + weiUsed = res.receipt.gasUsed * gasPrice; + console.log(' swapPiptToEth gasUsed', res.receipt.gasUsed, 'ethUsed(100 gwei)', web3.utils.fromWei(weiUsed.toString(), 'ether')); + balanceAfterWeiUsed = subBN(bobBalanceBefore, weiUsed); + + assert.equal(addBN(balanceAfterWeiUsed, ethOutAfterFee), await web3.eth.getBalance(bob)); }); }); }); diff --git a/test/IndicesSupplyRedeemZap.test.js b/test/IndicesSupplyRedeemZap.test.js index 496838bf..90d1d9ec 100644 --- a/test/IndicesSupplyRedeemZap.test.js +++ b/test/IndicesSupplyRedeemZap.test.js @@ -197,24 +197,28 @@ describe('IndicesSupplyRedeemZap', () => { const aliceEthToSwap = ether(10); const bobEthToSwap = ether(20); - const danUsdcToSwap = mwei(5000); - const carolUsdcToSwap = mwei(10000); - const usdcTokenCap = mwei(11000); + const danUsdcToSwap = ether(5000); + const carolUsdcToSwap = ether(10000); + const usdcTokenCap = ether(11000); - const erc20PiptSwap = await Erc20PiptSwap.new(this.weth.address, cvp.address, pool.address, zeroAddress, feeManager, { + const erc20PiptSwap = await Erc20PiptSwap.new(this.weth.address, usdc.address, cvp.address, pool.address, zeroAddress, feeManager, { from: minter, }); await erc20PiptSwap.fetchUnswapPairsFromFactory( this.uniswapFactory.address, + this.weth.address, tokens.map(t => t.address), + '0', { from: minter }, ); await erc20PiptSwap.setTokensSettings( tokens.map(t => t.address), pairs.map(p => p.address), + this.weth.address, pairs.map(() => true), + '0', { from: minter }, ); diff --git a/test/helpers/index.js b/test/helpers/index.js index b018f8fe..ee461830 100644 --- a/test/helpers/index.js +++ b/test/helpers/index.js @@ -1,6 +1,6 @@ const { ether: rEther } = require('@openzeppelin/test-helpers'); const TruffleContract = require('@nomiclabs/truffle-contract'); -const template = artifacts.require('Migrations'); +const template = artifacts.require('BPool'); const { promisify } = require('util'); const { assert } = require('chai'); const { web3 } = template;