diff --git a/contracts/interfaces/kyber.sol b/contracts/interfaces/kyber.sol index 3f8a393d7..aa7bbd385 100644 --- a/contracts/interfaces/kyber.sol +++ b/contracts/interfaces/kyber.sol @@ -121,4 +121,3 @@ interface IKyberFactory { interface IDMMPool { function kLast() external view returns (uint256); } - diff --git a/contracts/zapper-contract/SnowglobeZapperPangolin.sol b/contracts/zapper-contract/SnowglobeZapperPangolin.sol index 7f5933bc7..f8956eb66 100644 --- a/contracts/zapper-contract/SnowglobeZapperPangolin.sol +++ b/contracts/zapper-contract/SnowglobeZapperPangolin.sol @@ -12,9 +12,66 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { } + /** + * @notice Returns the token pair and vault at the snowglobe address + * @param snowglobe address of the lp pair + */ + function _getVaultPair(address snowglobe) internal view returns (IGlobe vault, IPangolinPair pair){ + + vault = IGlobe(snowglobe); + pair = IPangolinPair(vault.token()); + + require(pair.factory() == IPangolinPair(router).factory(), "Incompatible liquidity pair factory"); + } + + /** + * @notice Zaps into the liquidity pair with AVAX + * @param snowglobe + * @param tokenAmontOutMin + * @param tokenIn + */ + function zapInAVAX(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) external payable override { + require(msg.value >= minimumAmount, "Insignificant input amount"); + + // Get balance of native AVAX and wrap AVAX into ERC20 (WAVAX) + WAVAX(wavax).deposit{value: msg.value}(); + + // gets the token pair in the lp + (, IPangolinPair pair) = _getVaultPair(snowglobe); + + (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); + require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); + + address token0 = pair.token0(); // first token in the pool + address token1 = pair.token1(); // second token in the pool + + bool isInputA = token0 == tokenIn; + require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); + + // allows us to zapIn if avax isn't part of the original pair + if (tokenIn != wavax){ + uint256 _amount = IERC20(wavax).balanceOf(address(this)); + + // Use the kyber router to find a direct root from wavax to the desired tokenIn + // then swap the wavax for the tokenIn and add liquidity for the pool + uint256 swapAmountIn = _getSwapAmount(_amount, reserveA, reserveB); + _swapToken(wavax, tokenIn, swapAmountIn); + _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + } else { + _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + } + } + + /** + * @notice Zaps out of the liquidity pair to a desired token + * @param snowglobe + * @param withdrawAmount + * @param desiredToken + * @param desiredTokenOutMin + */ function zapOutAndSwap(address snowglobe, uint256 withdrawAmount, address desiredToken, uint256 desiredTokenOutMin) public override { - (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); + (IGlobe vault, IPangolinPair pair) = _getVaultPair(snowglobe); address token0 = pair.token0(); address token1 = pair.token1(); require(token0 == desiredToken || token1 == desiredToken, "desired token not present in liquidity pair"); @@ -24,6 +81,7 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { _removeLiquidity(address(pair), address(this)); address swapToken = token1 == desiredToken ? token0 : token1; + _approveTokenIfNeeded(swapToken, address(router)); address[] memory path = new address[](2); path[0] = swapToken; path[1] = desiredToken; @@ -36,22 +94,30 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { address(this), block.timestamp ); - _returnAssets(path); } + /** + * @notice Stakes into the liquidty pair with either token0 or token1 in the liquidty pair + * @param snowglobe + * @param tokenAmountOutMin + * @param tokenIn + */ function _swapAndStake(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) public override { - (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); + (IGlobe vault, IPangolinPair pair) = _getVaultPair(snowglobe); (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); - bool isInputA = pair.token0() == tokenIn; - require(isInputA || pair.token1() == tokenIn, "Input token not present in liquidity pair"); + address token0 = pair.token0(); // first token in the pool + address token1 = pair.token1(); // second token in the pool + + bool isInputA = token0 == tokenIn; + require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); address[] memory path = new address[](2); path[0] = tokenIn; - path[1] = isInputA ? pair.token1() : pair.token0(); + path[1] = isInputA ? token1 : token0; uint256 fullInvestment = IERC20(tokenIn).balanceOf(address(this)); uint256 swapAmountIn; @@ -62,7 +128,7 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { } _approveTokenIfNeeded(path[0], address(router)); - uint256[] memory swappedAmounts = IPangolinRouter(router).swapExactTokensForTokens( + uint256[] memory swappedAmounts = IPangolinRouter(router).swapExactTokensForTokens( swapAmountIn, tokenAmountOutMin, path, @@ -117,7 +183,7 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { } function estimateSwap(address snowglobe, address tokenIn, uint256 fullInvestmentIn) public view returns (uint256 swapAmountIn, uint256 swapAmountOut, address swapTokenOut){ - (, IUniPair pair) = _getVaultPair(snowglobe); + (, IPangolinPair pair) = _getVaultPair(snowglobe); bool isInputA = pair.token0() == tokenIn; require(isInputA || pair.token1() == tokenIn, "Input token not present in liquidity pair"); @@ -134,4 +200,26 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { swapTokenOut = isInputA ? pair.token1() : pair.token0(); } + function zapOut(address snowglobe, uint256 withdrawAmount) external override { + (IGlobe vault, IPangolinPair pair) = _getVaultPair(snowglobe); + address token0 = pair.token0(); + address token1 = pair.token1(); + + IERC20(snowglobe).safeTransferFrom(msg.sender, address(this), withdrawAmount); + vault.withdraw(withdrawAmount); + + if (pair.token0() != wavax && pair.token1() != wavax) { + return _removeLiquidity(address(pair), msg.sender); + } + + + _removeLiquidity(address(pair), address(this)); + + address[] memory tokens = new address[](2); + tokens[0] = token0; + tokens[1] = token1; + + _returnAssets(tokens); + } + } \ No newline at end of file diff --git a/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol b/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol index ff1f3ee38..740179237 100644 --- a/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol +++ b/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol @@ -12,6 +12,63 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { } + /** + * @notice Returns the token pair and vault at the snowglobe address + * @param snowglobe address of the lp pair + */ + function _getVaultPair(address snowglobe) internal view returns (IGlobe vault, IJoePair pair){ + + vault = IGlobe(snowglobe); + pair = IJoePair(vault.token()); + + require(pair.factory() == IJoePair(router).factory(), "Incompatible liquidity pair factory"); + } + + /** + * @notice Zaps into the liquidity pair with AVAX + * @param snowglobe + * @param tokenAmontOutMin + * @param tokenIn + */ + function zapInAVAX(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) external payable override { + require(msg.value >= minimumAmount, "Insignificant input amount"); + + // Get balance of native AVAX and wrap AVAX into ERC20 (WAVAX) + WAVAX(wavax).deposit{value: msg.value}(); + + // gets the token pair in the lp + (, IJoePair pair) = _getVaultPair(snowglobe); + + (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); + require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); + + address token0 = pair.token0(); // first token in the pool + address token1 = pair.token1(); // second token in the pool + + bool isInputA = token0 == tokenIn; + require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); + + // allows us to zapIn if avax isn't part of the original pair + if (tokenIn != wavax){ + uint256 _amount = IERC20(wavax).balanceOf(address(this)); + + // Use the kyber router to find a direct root from wavax to the desired tokenIn + // then swap the wavax for the tokenIn and add liquidity for the pool + uint256 swapAmountIn = _getSwapAmount(_amount, reserveA, reserveB); + _swapToken(wavax, tokenIn, swapAmountIn); + _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + } else { + _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + } + } + + /** + * @notice Zaps out of the liquidity pair to a desired token + * @param snowglobe + * @param withdrawAmount + * @param desiredToken + * @param desiredTokenOutMin + */ function zapOutAndSwap(address snowglobe, uint256 withdrawAmount, address desiredToken, uint256 desiredTokenOutMin) public override { (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); address token0 = pair.token0(); @@ -23,6 +80,7 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { _removeLiquidity(address(pair), address(this)); address swapToken = token1 == desiredToken ? token0 : token1; + _approveTokenIfNeeded(swapToken, address(router)); address[] memory path = new address[](2); path[0] = swapToken; path[1] = desiredToken; @@ -35,22 +93,30 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { address(this), block.timestamp ); - _returnAssets(path); } + /** + * @notice Stakes into the liquidty pair with either token0 or token1 in the liquidty pair + * @param snowglobe + * @param tokenAmountOutMin + * @param tokenIn + */ function _swapAndStake(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) public override { (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); - bool isInputA = pair.token0() == tokenIn; - require(isInputA || pair.token1() == tokenIn, "Input token not present in liquidity pair"); + address token0 = pair.token0(); // first token in the pool + address token1 = pair.token1(); // second token in the pool + + bool isInputA = token0 == tokenIn; + require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); address[] memory path = new address[](2); path[0] = tokenIn; - path[1] = isInputA ? pair.token1() : pair.token0(); + path[1] = isInputA ? token1 : token0; uint256 fullInvestment = IERC20(tokenIn).balanceOf(address(this)); uint256 swapAmountIn; @@ -61,7 +127,7 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { } _approveTokenIfNeeded(path[0], address(router)); - uint256[] memory swappedAmounts = IJoeRouter(router) + uint256[] memory swappedAmounts = IJoeRouter(router) .swapExactTokensForTokens( swapAmountIn, tokenAmountOutMin, @@ -85,9 +151,6 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { _approveTokenIfNeeded(address(pair), address(vault)); vault.deposit(amountLiquidity); - //add to guage if possible instead of returning to user, and so no receipt token - vault.safeTransfer(msg.sender, vault.balanceOf(address(this))); - //taking receipt token and sending back to user vault.safeTransfer(msg.sender, vault.balanceOf(address(this))); @@ -120,7 +183,7 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { } function estimateSwap(address snowglobe, address tokenIn, uint256 fullInvestmentIn) public view returns (uint256 swapAmountIn, uint256 swapAmountOut, address swapTokenOut){ - (, IUniPair pair) = _getVaultPair(snowglobe); + (, IJoePair pair) = _getVaultPair(snowglobe); bool isInputA = pair.token0() == tokenIn; require(isInputA || pair.token1() == tokenIn, "Input token not present in liquidity pair"); diff --git a/contracts/zapper-contract/SnowglobeZapperVector.sol b/contracts/zapper-contract/SnowglobeZapperVector.sol new file mode 100644 index 000000000..733b929cf --- /dev/null +++ b/contracts/zapper-contract/SnowglobeZapperVector.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +import "./zapper-base.sol"; +import "../interfaces/gaugeproxy.sol"; + +contract SnowglobeZapAvaxVector is ZapperBase { + address public gaugeProxy = 0x215D5eDEb6A6a3f84AE9d72962FEaCCdF815BF27; + + constructor() + public ZapperBase(0x60aE616a2155Ee3d9A68541Ba4544862310933d4){ + + } + + /** + * @notice Returns the token pair and vault at the snowglobe address + * @param snowglobe address of the lp pair + */ + function _getVaultPair(address snowglobe) internal view returns (IGlobe vault, IJoePair pair){ + + vault = IGlobe(snowglobe); + pair = IJoePair(vault.token()); + + require(pair.factory() == IJoePair(router).factory(), "Incompatible liquidity pair factory"); + } + + /** + * @notice Zaps into the liquidity pair with AVAX + * @param snowglobe + * @param tokenAmontOutMin + * @param tokenIn + */ + function zapInAVAX(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) external payable override { + require(msg.value >= minimumAmount, "Insignificant input amount"); + + // Get balance of native AVAX and wrap AVAX into ERC20 (WAVAX) + WAVAX(wavax).deposit{value: msg.value}(); + + // gets the token pair in the lp + (, IJoePair pair) = _getVaultPair(snowglobe); + + (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); + require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); + + address token0 = pair.token0(); // first token in the pool + address token1 = pair.token1(); // second token in the pool + + bool isInputA = token0 == tokenIn; + require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); + + // allows us to zapIn if avax isn't part of the original pair + if (tokenIn != wavax){ + uint256 _amount = IERC20(wavax).balanceOf(address(this)); + + // Use the kyber router to find a direct root from wavax to the desired tokenIn + // then swap the wavax for the tokenIn and add liquidity for the pool + uint256 swapAmountIn = _getSwapAmount(_amount, reserveA, reserveB); + _swapToken(wavax, tokenIn, swapAmountIn); + _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + } else { + _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + } + } + + /** + * @notice Zaps out of the liquidity pair to a desired token + * @param snowglobe + * @param withdrawAmount + * @param desiredToken + * @param desiredTokenOutMin + */ + function zapOutAndSwap(address snowglobe, uint256 withdrawAmount, address desiredToken, uint256 desiredTokenOutMin) public override { + (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); + address token0 = pair.token0(); + address token1 = pair.token1(); + require(token0 == desiredToken || token1 == desiredToken, "desired token not present in liquidity pair"); + + vault.safeTransferFrom(msg.sender, address(this), withdrawAmount); + vault.withdraw(withdrawAmount); + _removeLiquidity(address(pair), address(this)); + + address swapToken = token1 == desiredToken ? token0 : token1; + _approveTokenIfNeeded(swapToken, address(router)); + address[] memory path = new address[](2); + path[0] = swapToken; + path[1] = desiredToken; + + _approveTokenIfNeeded(path[0], address(router)); + IJoeRouter(router).swapExactTokensForTokens( + IERC20(swapToken).balanceOf(address(this)), + desiredTokenOutMin, + path, + address(this), + block.timestamp + ); + + _returnAssets(path); + } + + /** + * @notice Stakes into the liquidty pair with either token0 or token1 in the liquidty pair + * @param snowglobe + * @param tokenAmountOutMin + * @param tokenIn + */ + function _swapAndStake(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) public override { + (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); + + (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); + require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); + + address token0 = pair.token0(); // first token in the pool + address token1 = pair.token1(); // second token in the pool + + bool isInputA = token0 == tokenIn; + require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); + + address[] memory path = new address[](2); + path[0] = tokenIn; + path[1] = isInputA ? token1 : token0; + + uint256 fullInvestment = IERC20(tokenIn).balanceOf(address(this)); + uint256 swapAmountIn; + if (isInputA) { + swapAmountIn = _getSwapAmount(fullInvestment, reserveA, reserveB); + } else { + swapAmountIn = _getSwapAmount(fullInvestment, reserveB, reserveA); + } + + _approveTokenIfNeeded(path[0], address(router)); + uint256[] memory swappedAmounts = IJoeRouter(router) + .swapExactTokensForTokens( + swapAmountIn, + tokenAmountOutMin, + path, + address(this), + block.timestamp + ); + + _approveTokenIfNeeded(path[1], address(router)); + (, , uint256 amountLiquidity) = IJoeRouter(router).addLiquidity( + path[0], + path[1], + fullInvestment.sub(swappedAmounts[0]), + swappedAmounts[1], + 1, + 1, + address(this), + block.timestamp + ); + + _approveTokenIfNeeded(address(pair), address(vault)); + vault.deposit(amountLiquidity); + + //taking receipt token and sending back to user + vault.safeTransfer(msg.sender, vault.balanceOf(address(this))); + + //interact with gauge proxy to get gauge address + address gaugeAddress = IGaugeProxyV2(gaugeProxy).getGauge(snowglobe); + + //deposit for into gauge + IGaugeV2(gaugeAddress).depositFor(vault.balanceOf(msg.sender), msg.sender); + + _returnAssets(path); + } + + function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view override returns (uint256 swapAmount) { + uint256 halfInvestment = investmentA.div(2); + uint256 nominator = IJoeRouter(router).getAmountOut( + halfInvestment, + reserveA, + reserveB + ); + uint256 denominator = IJoeRouter(router).quote( + halfInvestment, + reserveA.add(halfInvestment), + reserveB.sub(nominator) + ); + swapAmount = investmentA.sub( + Babylonian.sqrt( + (halfInvestment * halfInvestment * nominator) / denominator + ) + ); + } + + function estimateSwap(address snowglobe, address tokenIn, uint256 fullInvestmentIn) public view returns (uint256 swapAmountIn, uint256 swapAmountOut, address swapTokenOut){ + (, IJoePair pair) = _getVaultPair(snowglobe); + + bool isInputA = pair.token0() == tokenIn; + require(isInputA || pair.token1() == tokenIn, "Input token not present in liquidity pair"); + + (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); + (reserveA, reserveB) = isInputA ? (reserveA, reserveB) : (reserveB, reserveA); + + swapAmountIn = _getSwapAmount(fullInvestmentIn, reserveA, reserveB); + swapAmountOut = IJoeRouter(router).getAmountOut( + swapAmountIn, + reserveA, + reserveB + ); + swapTokenOut = isInputA ? pair.token1() : pair.token0(); + } +} \ No newline at end of file diff --git a/contracts/zapper-contract/zapper-base.sol b/contracts/zapper-contract/zapper-base.sol index e89ea883d..ede170c38 100644 --- a/contracts/zapper-contract/zapper-base.sol +++ b/contracts/zapper-contract/zapper-base.sol @@ -9,6 +9,7 @@ import "../lib/square-root.sol"; import "../interfaces/wavax.sol"; import "../interfaces/globe.sol"; import "../interfaces/uniAmm.sol"; +import "../interfaces/kyber.sol"; abstract contract ZapperBase { using SafeERC20 for IERC20; @@ -20,6 +21,9 @@ abstract contract ZapperBase { address public constant wavax = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7; + address public constant kyberFactory = 0x10908C875D865C66f271F5d3949848971c9595C9; + address public constant kyberRouter = 0x8Efa5A9AD6D594Cf76830267077B78cE0Bc5A5F8; + uint256 public constant minimumAmount = 1000; constructor(address _router) public { @@ -35,7 +39,7 @@ abstract contract ZapperBase { function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view virtual returns (uint256 swapAmount); - //returns DUST + // returns DUST function _returnAssets(address[] memory tokens) internal { uint256 balance; for (uint256 i; i < tokens.length; i++) { @@ -56,44 +60,106 @@ abstract contract ZapperBase { function _swapAndStake(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) public virtual; - function zapInAVAX(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) external payable{ - require(msg.value >= minimumAmount, "Insignificant input amount"); - - WAVAX(wavax).deposit{value: msg.value}(); - - // allows us to zapIn if avax isn't part of the original pair - if (tokenIn != wavax){ - uint256 _amount = IERC20(wavax).balanceOf(address(this)); - - (, IUniPair pair) = _getVaultPair(snowglobe); - - (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); - require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); - - bool isInputA = pair.token0() == tokenIn; - require(isInputA || pair.token1() == tokenIn, "Input token not present in liquidity pair"); - - address[] memory path = new address[](2); - path[0] = wavax; - path[1] = tokenIn; - - uint256 swapAmountIn; - - swapAmountIn = _getSwapAmount(_amount, reserveA, reserveB); - - _approveTokenIfNeeded(path[0], address(router)); - IUniAmmRouter(router).swapExactTokensForTokens( - swapAmountIn, - tokenAmountOutMin, - path, - address(this), - block.timestamp - ); - _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); - }else{ - _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + // finds the best pool path for swapping between tokens + function bestPoolForSwap(IERC20 _from, IERC20 _to) internal view returns(address){ + address[] memory poolsPath = IKyberFactory(kyberFactory).getPools(_from, _to); + + address bestPool; + uint256 highestKLast = 0; + uint256 bestIndex = 0; + for (uint i = 0; i < poolsPath.length; i++) { + uint256 currentKLast = IDMMPool(poolsPath[i]).kLast(); + if (currentKLast > highestKLast) { + highestKLast = currentKLast; + bestIndex = i; + } + } + + // handle case if highestKLast is 0 (no liquidity) + if (highestKLast == 0) { + bestPool = address(0); + } else { + bestPool = poolsPath[bestIndex]; } + + return bestPool; } + /// @notice swaps from one token to another using the kyber Router + /// @param _from the token address we're swapping from + /// @param _to the token we're swapping to + /// @param _amount the amount of _from we are using for the swap + function _swapToken(address _from, address _to, uint256 _amount) internal returns (IERC20[] memory path, uint256[] memory amounts){ + require(_to != address(0), "Needs a valid address for swapping!"); + require(_to != _from, "Token address we're swapping to and from must be different!"); + + address[] memory poolsPath; + + if (_from == wavax || _to == wavax) { + path = new IERC20[](2); + path[0] = IERC20(_from); + path[1] = IERC20(_to); + + poolsPath = new address[](1); + poolsPath[0] = bestPoolForSwap(IERC20(_from), IERC20(_to)); + + + } else { + path = new IERC20[](3); + path[0] = IERC20(_from); + path[1] = IERC20(wavax); + path[2] = IERC20(_to); + + poolsPath = new address[](2); + poolsPath[0] = bestPoolForSwap(IERC20(_from), IERC20(wavax)); + poolsPath[1] = bestPoolForSwap(IERC20(wavax), IERC20(_to)); + } + + IERC20(_from).safeApprove(kyberRouter, 0); + IERC20(_from).safeApprove(kyberRouter, _amount); + + amounts = IKyber(kyberRouter).swapExactTokensForTokens( + _amount, + 0, + poolsPath, + path, + address(this), + now + 60 + ); + } + + function zapInAVAX(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) external payable virtual; + + // function zapInAVAX(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) external payable{ + // require(msg.value >= minimumAmount, "Insignificant input amount"); + + // // Get balance of native AVAX and wrap AVAX into ERC20 (WAVAX) + // WAVAX(wavax).deposit{value: msg.value}(); + + // // gets the token pair in the lp + // (, IUniPair pair) = _getVaultPair(snowglobe); + + // (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); + // require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); + + // address token0 = pair.token0(); // first token in the pool + // address token1 = pair.token1(); // second token in the pool + + // bool isInputA = token0 == tokenIn; + // require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); + + // // allows us to zapIn if avax isn't part of the original pair + // if (tokenIn != wavax){ + // uint256 _amount = IERC20(wavax).balanceOf(address(this)); + + // // Use the kyber router to find a direct root from wavax to the desired tokenIn + // // then swap the wavax for the tokenIn and add liquidity for the pool + // uint256 swapAmountIn = _getSwapAmount(_amount, reserveA, reserveB); + // _swapToken(wavax, tokenIn, swapAmountIn); + // _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + // }else{ + // _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + // } + // } // transfers tokens from msg.sender to this contract function zapIn(address snowglobe, uint256 tokenAmountOutMin, address tokenIn, uint256 tokenInAmount) external { @@ -119,15 +185,13 @@ abstract contract ZapperBase { require(amount1 >= minimumAmount, "Router: INSUFFICIENT_B_AMOUNT"); } - function _getVaultPair(address snowglobe) internal view returns (IGlobe vault, IUniPair pair){ + // function _getVaultPair(address snowglobe) internal view returns (IGlobe vault, IUniPair pair){ - vault = IGlobe(snowglobe); - pair = IUniPair(vault.token()); + // vault = IGlobe(snowglobe); + // pair = IUniPair(vault.token()); - - - require(pair.factory() == IUniPair(router).factory(), "Incompatible liquidity pair factory"); - } + // require(pair.factory() == IUniPair(router).factory(), "Incompatible liquidity pair factory"); + // } function _approveTokenIfNeeded(address token, address spender) internal { if (IERC20(token).allowance(address(this), spender) == 0) { @@ -135,23 +199,27 @@ abstract contract ZapperBase { } } - function zapOut(address snowglobe, uint256 withdrawAmount) external { - (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); + function zapOut(address snowglobe, uint256 withdrawAmount) external virtual; - IERC20(snowglobe).safeTransferFrom(msg.sender, address(this), withdrawAmount); - vault.withdraw(withdrawAmount); + // function zapOut(address snowglobe, uint256 withdrawAmount) external { + // (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); + // address token0 = pair.token0(); + // address token1 = pair.token1(); - if (pair.token0() != wavax && pair.token1() != wavax) { - return _removeLiquidity(address(pair), msg.sender); - } + // IERC20(snowglobe).safeTransferFrom(msg.sender, address(this), withdrawAmount); + // vault.withdraw(withdrawAmount); + // if (pair.token0() != wavax && pair.token1() != wavax) { + // return _removeLiquidity(address(pair), msg.sender); + // } - _removeLiquidity(address(pair), address(this)); - address[] memory tokens = new address[](2); - tokens[0] = pair.token0(); - tokens[1] = pair.token1(); + // _removeLiquidity(address(pair), address(this)); - _returnAssets(tokens); - } + // address[] memory tokens = new address[](2); + // tokens[0] = token0; + // tokens[1] = token1; + + // _returnAssets(tokens); + // } } \ No newline at end of file diff --git a/test/Zapper/vector-zapper-test.ts b/test/Zapper/vector-zapper-test.ts new file mode 100644 index 000000000..6391f92a7 --- /dev/null +++ b/test/Zapper/vector-zapper-test.ts @@ -0,0 +1,31 @@ + +/*** + *======== HOW TO USE THIS FILE ======== + * + * ***/ + + import { doZapperTests } from "./../zapper-test"; + + const tests = [ + { + name: "VtxxPtpPtp", + snowglobeAddress: "0xFFa18894152f4B1869C9dfEFB28459468f065e31", + gaugeAddress: "0xFc371bA1E7874Ad893408D7B581F3c8471F03D2C", + controller: "vector" + }, + + ]; + + + describe("Vector Zapper Tests", function() { + for (const test of tests) { + doZapperTests( + test.name, + test.snowglobeAddress, + "Vector", + test.gaugeAddress, + test.controller + ); + } + }); + \ No newline at end of file