From d08b0bb95eebf395a7646a508e7c51659b30dc69 Mon Sep 17 00:00:00 2001 From: Abigail Date: Sun, 3 Apr 2022 20:01:59 -0500 Subject: [PATCH 1/6] zapper update --- .../zapper-contract/SnowglobeZapperVector.sol | 159 ++++++++++++++++ .../zapper-contract/zapper-base-vector.sol | 172 ++++++++++++++++++ 2 files changed, 331 insertions(+) create mode 100644 contracts/zapper-contract/SnowglobeZapperVector.sol create mode 100644 contracts/zapper-contract/zapper-base-vector.sol diff --git a/contracts/zapper-contract/SnowglobeZapperVector.sol b/contracts/zapper-contract/SnowglobeZapperVector.sol new file mode 100644 index 000000000..2d2fac23b --- /dev/null +++ b/contracts/zapper-contract/SnowglobeZapperVector.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +import "./zapper-base-vector.sol"; +import "../interfaces/gaugeproxy.sol"; + +contract SnowglobeZapAvaxVector is ZapperBaseVector { + address public gaugeProxy = 0x215D5eDEb6A6a3f84AE9d72962FEaCCdF815BF27; + + constructor() + public ZapperBaseVector(0x60aE616a2155Ee3d9A68541Ba4544862310933d4){ + + } + + function zapOutSwapPath(address swapToken, address desiredToken) internal view returns (address[] memory path) { + /// @notice if we want to do a direct swap from xptp to ptp, or ptp to xptp, then we just need a direct path + /// between the two tokens + if (((swapToken == xptp) && (desiredToken == ptp)) || ((swapToken == ptp) && (desiredToken == xptp))) { + path = new address[](2); + path[0] = swapToken; + path[1] = desiredToken; + } + /// @notice if we want to swap from xptp then we have to convert to ptp before going to the desired token + /// and vice versa if we want to get xptp, we must convert from ptp + else if (((swapToken == xptp) && (desiredToken != ptp)) || ((desiredToken == xptp) && (swapToken != ptp))) { + path = new address[](3); + path[0] = swapToken; + path[1] = ptp; + path[2] = desiredToken; + } + else { + path = new address[](2); + path[0] = swapToken; + path[1] = desiredToken; + } + } + + 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; + address[] memory path = zapOutSwapPath(swapToken, desiredToken); + _approveTokenIfNeeded(path[0], address(router)); + IJoeRouter(router).swapExactTokensForTokens( + IERC20(swapToken).balanceOf(address(this)), + desiredTokenOutMin, + path, + address(this), + block.timestamp + ); + + _returnAssets(path); + } + + 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[] memory path = new address[](2); + path[0] = tokenIn; + path[1] = isInputA ? pair.token1() : pair.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); + + //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))); + + //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){ + (, IUniPair 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-vector.sol b/contracts/zapper-contract/zapper-base-vector.sol new file mode 100644 index 000000000..960597a06 --- /dev/null +++ b/contracts/zapper-contract/zapper-base-vector.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +import "../lib/safe-math.sol"; +import "../lib/erc20.sol"; +import "../interfaces/pangolin.sol"; +import "../interfaces/joe.sol"; +import "../lib/square-root.sol"; +import "../interfaces/wavax.sol"; +import "../interfaces/globe.sol"; +import "../interfaces/uniAmm.sol"; + +abstract contract ZapperBaseVector { + using SafeERC20 for IERC20; + using Address for address; + using SafeMath for uint256; + using SafeERC20 for IGlobe; + + address public router; + + address public constant wavax = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7; + address public constant xptp = 0x060556209E507d30f2167a101bFC6D256Ed2f3e1; + address public constant ptp = 0x22d4002028f537599bE9f666d1c4Fa138522f9c8; + + uint256 public constant minimumAmount = 1000; + + constructor(address _router) public { + // Safety checks to ensure WAVAX token address + WAVAX(wavax).deposit{value: 0}(); + WAVAX(wavax).withdraw(0); + router = _router; + } + + receive() external payable { + assert(msg.sender == wavax); + } + + function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view virtual returns (uint256 swapAmount); + + //returns DUST + function _returnAssets(address[] memory tokens) internal { + uint256 balance; + for (uint256 i; i < tokens.length; i++) { + balance = IERC20(tokens[i]).balanceOf(address(this)); + if (balance > 0) { + if (tokens[i] == wavax) { + WAVAX(wavax).withdraw(balance); + (bool success, ) = msg.sender.call{value: balance}( + new bytes(0) + ); + require(success, "AVAX transfer failed"); + } else { + IERC20(tokens[i]).safeTransfer(msg.sender, balance); + } + } + } + } + + function _swapAndStake(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) public virtual; + + function zapInAVAXPath(address tokenIn) pure internal returns (address[] memory path) { + if (tokenIn == xptp) { + path = new address[](3); + path[0] = wavax; + path[1] = ptp; + path[2] = tokenIn; + } + else { + path = new address[](2); + path[0] = wavax; + path[1] = tokenIn; + } + } + + 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"); + + address token0 = pair.token0(); + address token1 = pair.token1(); + + bool isInputA = token0 == tokenIn; + require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); + + address[] memory path = zapInAVAXPath(tokenIn); + + uint256 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); + } + } + + // transfers tokens from msg.sender to this contract + function zapIn(address snowglobe, uint256 tokenAmountOutMin, address tokenIn, uint256 tokenInAmount) external { + require(tokenInAmount >= minimumAmount, "Insignificant input amount"); + require(IERC20(tokenIn).allowance(msg.sender, address(this)) >= tokenInAmount, "Input token is not approved"); + + // transfer token + IERC20(tokenIn).safeTransferFrom( + msg.sender, + address(this), + tokenInAmount + ); + _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); + } + + function zapOutAndSwap(address snowglobe, uint256 withdrawAmount, address desiredToken, uint256 desiredTokenOutMin) public virtual; + + function _removeLiquidity(address pair, address to) internal { + IERC20(pair).safeTransfer(pair, IERC20(pair).balanceOf(address(this))); + (uint256 amount0, uint256 amount1) = IUniPair(pair).burn(to); + + require(amount0 >= minimumAmount, "Router: INSUFFICIENT_A_AMOUNT"); + require(amount1 >= minimumAmount, "Router: INSUFFICIENT_B_AMOUNT"); + } + + function _getVaultPair(address snowglobe) internal view returns (IGlobe vault, IUniPair pair){ + + vault = IGlobe(snowglobe); + pair = IUniPair(vault.token()); + + + + 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) { + IERC20(token).safeApprove(spender, uint256(~0)); + } + } + + function zapOut(address snowglobe, uint256 withdrawAmount) external { + (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); + + 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(); + + _returnAssets(tokens); + } +} \ No newline at end of file From 2ec6bea880455f5bbe61c37691fa028c4d65e590 Mon Sep 17 00:00:00 2001 From: Abigail Date: Mon, 4 Apr 2022 11:50:46 -0500 Subject: [PATCH 2/6] zapper --- .../zapper-contract/SnowglobeZapperVector.sol | 19 +++++++----- test/Zapper/vector-zapper-test.ts | 31 +++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 test/Zapper/vector-zapper-test.ts diff --git a/contracts/zapper-contract/SnowglobeZapperVector.sol b/contracts/zapper-contract/SnowglobeZapperVector.sol index 2d2fac23b..32fdb8cdb 100644 --- a/contracts/zapper-contract/SnowglobeZapperVector.sol +++ b/contracts/zapper-contract/SnowglobeZapperVector.sol @@ -12,7 +12,7 @@ contract SnowglobeZapAvaxVector is ZapperBaseVector { } - function zapOutSwapPath(address swapToken, address desiredToken) internal view returns (address[] memory path) { + function zapOutSwapPath(address swapToken, address desiredToken) pure internal returns (address[] memory path) { /// @notice if we want to do a direct swap from xptp to ptp, or ptp to xptp, then we just need a direct path /// between the two tokens if (((swapToken == xptp) && (desiredToken == ptp)) || ((swapToken == ptp) && (desiredToken == xptp))) { @@ -65,12 +65,15 @@ contract SnowglobeZapAvaxVector is ZapperBaseVector { (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(); + address token1 = pair.token1(); + + 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; @@ -94,10 +97,10 @@ contract SnowglobeZapAvaxVector is ZapperBaseVector { (, , uint256 amountLiquidity) = IJoeRouter(router).addLiquidity( path[0], path[1], - fullInvestment.sub(swappedAmounts[0]), - swappedAmounts[1], - 1, - 1, + IERC20(path[0]).balanceOf(address(this)), + IERC20(path[1]).balanceOf(address(this)), + 0, + 0, address(this), block.timestamp ); 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 From 897392b461d1c3590d407d3cfa683a8a362d3614 Mon Sep 17 00:00:00 2001 From: Abigail Date: Mon, 4 Apr 2022 13:05:36 -0500 Subject: [PATCH 3/6] pause zapper --- contracts/interfaces/kyber.sol | 123 ++++++++++++++++++++++ contracts/zapper-contract/zapper-base.sol | 11 +- 2 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 contracts/interfaces/kyber.sol diff --git a/contracts/interfaces/kyber.sol b/contracts/interfaces/kyber.sol new file mode 100644 index 000000000..adf1545d4 --- /dev/null +++ b/contracts/interfaces/kyber.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.7; +pragma experimental ABIEncoderV2; + +import "../lib/erc20.sol"; + +interface IKyber { + function addLiquidityETH( + address token, + address pool, + uint256 amountTokenDesired, + uint256 amountTokenMin, + uint256 amountETHMin, + uint256[2] calldata vReserveRatioBounds, + address to, + uint256 deadline + ) + external + payable + returns ( + uint256 amountToken, + uint256 amountETH, + uint256 liquidity + ); + + function addLiquidity( + address tokenA, + address tokenB, + address pool, + uint256 amountADesired, + uint256 amountBDesired, + uint256 amountAMin, + uint256 amountBMin, + uint256[2] calldata vReserveRatioBounds, + address to, + uint256 deadline + ) + external + returns ( + uint256 amountA, + uint256 amountB, + uint256 liquidity + ); + + function getTradeInfo() external view returns ( + uint112 _reserve0, + uint112 _reserve1, + uint112 _vReserve0, + uint112 _vReserve1, + uint256 feeInPrecision + ); + + function getReserves() external view returns (uint112 _reserve0, uint112 _reserve1); + + /** + * @dev Deposit tokens to accumulate rewards + * @param _pid: id of the pool + * @param _amount: amount of stakeToken to be deposited + * @param _shouldHarvest: whether to harvest the reward or not + */ + function deposit( + uint256 _pid, + uint256 _amount, + bool _shouldHarvest + ) external; + + /** + * @dev Get pending rewards of a user from a pool, mostly for front-end + * @param _pid: id of the pool + * @param _user: user to check for pending rewards + */ + function pendingRewards(uint256 _pid, address _user) + external view + returns (uint256[] memory rewards); + + /** + * @dev Return user's info including deposited amount and reward data + */ + function getUserInfo(uint256 _pid, address _account) + external + view + returns ( + uint256 amount, + uint256[] memory unclaimedRewards, + uint256[] memory lastRewardPerShares + ); + + /** + * @dev Withdraw token (of the sender) from pool, also harvest rewards + * @param _pid: id of the pool + * @param _amount: amount of stakeToken to withdraw + */ + function withdraw(uint256 _pid, uint256 _amount) external; + + function harvest(uint256 _pid) external; + + function swapExactTokensForTokens( + uint256 amountIn, + uint256 amountOutMin, + address[] calldata poolsPath, + IERC20[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); +} + +interface IRewardLocker { + /** + * @notice Allow a user to vest with specific schedule + */ + function vestScheduleAtIndices(address token, uint256[] calldata indexes) external returns (uint256); + + function accountVestedBalance(address, address) external returns(uint256); +} + +interface IKyberFactory { + function getPools(IERC20 token0, IERC20 token1) external view returns (address[] memory _tokenPools); +} + +interface IDMMPool { + function kLast() external view returns (uint256); +} \ No newline at end of file diff --git a/contracts/zapper-contract/zapper-base.sol b/contracts/zapper-contract/zapper-base.sol index e89ea883d..a0b43126d 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; @@ -35,7 +36,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++) { @@ -59,6 +60,7 @@ abstract contract ZapperBase { 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}(); // allows us to zapIn if avax isn't part of the original pair @@ -70,8 +72,11 @@ abstract contract ZapperBase { (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] = wavax; From 2b3111c2cea9e31cbcd48b3e5276f142287c2973 Mon Sep 17 00:00:00 2001 From: Abigail Date: Mon, 4 Apr 2022 15:58:35 -0500 Subject: [PATCH 4/6] ready for review --- .../SnowglobeZapperPangolin.sol | 37 ++-- .../SnowglobeZapperTraderJoe.sol | 38 ++-- .../zapper-contract/SnowglobeZapperVector.sol | 63 ++----- .../zapper-contract/zapper-base-vector.sol | 172 ------------------ contracts/zapper-contract/zapper-base.sol | 103 ++++++++--- 5 files changed, 119 insertions(+), 294 deletions(-) delete mode 100644 contracts/zapper-contract/zapper-base-vector.sol diff --git a/contracts/zapper-contract/SnowglobeZapperPangolin.sol b/contracts/zapper-contract/SnowglobeZapperPangolin.sol index 7f5933bc7..f712b6fdf 100644 --- a/contracts/zapper-contract/SnowglobeZapperPangolin.sol +++ b/contracts/zapper-contract/SnowglobeZapperPangolin.sol @@ -24,18 +24,8 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { _removeLiquidity(address(pair), address(this)); address swapToken = token1 == desiredToken ? token0 : token1; - address[] memory path = new address[](2); - path[0] = swapToken; - path[1] = desiredToken; - - _approveTokenIfNeeded(path[0], address(router)); - IPangolinRouter(router).swapExactTokensForTokens( - IERC20(swapToken).balanceOf(address(this)), - desiredTokenOutMin, - path, - address(this), - block.timestamp - ); + _approveTokenIfNeeded(swapToken, address(router)); + (IERC20[] memory path, ) = _swapToken(swapToken, desiredToken, IERC20(swapToken).balanceOf(address(this))); _returnAssets(path); } @@ -46,12 +36,15 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { (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,20 +55,14 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { } _approveTokenIfNeeded(path[0], address(router)); - uint256[] memory swappedAmounts = IPangolinRouter(router).swapExactTokensForTokens( - swapAmountIn, - tokenAmountOutMin, - path, - address(this), - block.timestamp - ); + (IERC20[] memory paths, uint256[] memory amounts) = _swapToken(path[0], path[1] , swapAmountIn); _approveTokenIfNeeded(path[1], address(router)); (, , uint256 amountLiquidity) = IPangolinRouter(router).addLiquidity( path[0], path[1], - fullInvestment.sub(swappedAmounts[0]), - swappedAmounts[1], + fullInvestment.sub(amounts[0]), + amounts[1], 1, 1, address(this), @@ -94,7 +81,7 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { //deposit for into gauge IGaugeV2(gaugeAddress).depositFor(vault.balanceOf(msg.sender), msg.sender); - _returnAssets(path); + _returnAssets(paths); } function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view override returns (uint256 swapAmount) { diff --git a/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol b/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol index ff1f3ee38..90b1204c9 100644 --- a/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol +++ b/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol @@ -23,18 +23,8 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { _removeLiquidity(address(pair), address(this)); address swapToken = token1 == desiredToken ? token0 : token1; - 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 - ); + _approveTokenIfNeeded(swapToken, address(router)); + (IERC20[] memory path, ) = _swapToken(swapToken, desiredToken, IERC20(swapToken).balanceOf(address(this))); _returnAssets(path); } @@ -45,12 +35,15 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { (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,21 +54,14 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { } _approveTokenIfNeeded(path[0], address(router)); - uint256[] memory swappedAmounts = IJoeRouter(router) - .swapExactTokensForTokens( - swapAmountIn, - tokenAmountOutMin, - path, - address(this), - block.timestamp - ); + (IERC20[] memory paths, uint256[] memory amounts) = _swapToken(path[0], path[1] , swapAmountIn); _approveTokenIfNeeded(path[1], address(router)); (, , uint256 amountLiquidity) = IJoeRouter(router).addLiquidity( path[0], path[1], - fullInvestment.sub(swappedAmounts[0]), - swappedAmounts[1], + fullInvestment.sub(amounts[0]), + amounts[1], 1, 1, address(this), @@ -97,7 +83,7 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { //deposit for into gauge IGaugeV2(gaugeAddress).depositFor(vault.balanceOf(msg.sender), msg.sender); - _returnAssets(path); + _returnAssets(paths); } function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view override returns (uint256 swapAmount) { diff --git a/contracts/zapper-contract/SnowglobeZapperVector.sol b/contracts/zapper-contract/SnowglobeZapperVector.sol index 32fdb8cdb..de6874ffa 100644 --- a/contracts/zapper-contract/SnowglobeZapperVector.sol +++ b/contracts/zapper-contract/SnowglobeZapperVector.sol @@ -1,40 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.6.7; -import "./zapper-base-vector.sol"; +import "./zapper-base.sol"; import "../interfaces/gaugeproxy.sol"; -contract SnowglobeZapAvaxVector is ZapperBaseVector { +contract SnowglobeZapAvaxVector is ZapperBase { address public gaugeProxy = 0x215D5eDEb6A6a3f84AE9d72962FEaCCdF815BF27; constructor() - public ZapperBaseVector(0x60aE616a2155Ee3d9A68541Ba4544862310933d4){ + public ZapperBase(0x60aE616a2155Ee3d9A68541Ba4544862310933d4){ } - function zapOutSwapPath(address swapToken, address desiredToken) pure internal returns (address[] memory path) { - /// @notice if we want to do a direct swap from xptp to ptp, or ptp to xptp, then we just need a direct path - /// between the two tokens - if (((swapToken == xptp) && (desiredToken == ptp)) || ((swapToken == ptp) && (desiredToken == xptp))) { - path = new address[](2); - path[0] = swapToken; - path[1] = desiredToken; - } - /// @notice if we want to swap from xptp then we have to convert to ptp before going to the desired token - /// and vice versa if we want to get xptp, we must convert from ptp - else if (((swapToken == xptp) && (desiredToken != ptp)) || ((desiredToken == xptp) && (swapToken != ptp))) { - path = new address[](3); - path[0] = swapToken; - path[1] = ptp; - path[2] = desiredToken; - } - else { - path = new address[](2); - path[0] = swapToken; - path[1] = desiredToken; - } - } - function zapOutAndSwap(address snowglobe, uint256 withdrawAmount, address desiredToken, uint256 desiredTokenOutMin) public override { (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); address token0 = pair.token0(); @@ -46,15 +23,8 @@ contract SnowglobeZapAvaxVector is ZapperBaseVector { _removeLiquidity(address(pair), address(this)); address swapToken = token1 == desiredToken ? token0 : token1; - address[] memory path = zapOutSwapPath(swapToken, desiredToken); - _approveTokenIfNeeded(path[0], address(router)); - IJoeRouter(router).swapExactTokensForTokens( - IERC20(swapToken).balanceOf(address(this)), - desiredTokenOutMin, - path, - address(this), - block.timestamp - ); + _approveTokenIfNeeded(swapToken, address(router)); + (IERC20[] memory path, ) = _swapToken(swapToken, desiredToken, IERC20(swapToken).balanceOf(address(this))); _returnAssets(path); } @@ -65,8 +35,8 @@ contract SnowglobeZapAvaxVector is ZapperBaseVector { (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); - address token0 = pair.token0(); - address token1 = pair.token1(); + 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"); @@ -84,23 +54,16 @@ contract SnowglobeZapAvaxVector is ZapperBaseVector { } _approveTokenIfNeeded(path[0], address(router)); - uint256[] memory swappedAmounts = IJoeRouter(router) - .swapExactTokensForTokens( - swapAmountIn, - tokenAmountOutMin, - path, - address(this), - block.timestamp - ); + (IERC20[] memory paths, uint256[] memory amounts) = _swapToken(path[0], path[1] , swapAmountIn); _approveTokenIfNeeded(path[1], address(router)); (, , uint256 amountLiquidity) = IJoeRouter(router).addLiquidity( path[0], path[1], - IERC20(path[0]).balanceOf(address(this)), - IERC20(path[1]).balanceOf(address(this)), - 0, - 0, + fullInvestment.sub(amounts[0]), + amounts[1], + 1, + 1, address(this), block.timestamp ); @@ -120,7 +83,7 @@ contract SnowglobeZapAvaxVector is ZapperBaseVector { //deposit for into gauge IGaugeV2(gaugeAddress).depositFor(vault.balanceOf(msg.sender), msg.sender); - _returnAssets(path); + _returnAssets(paths); } function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view override returns (uint256 swapAmount) { diff --git a/contracts/zapper-contract/zapper-base-vector.sol b/contracts/zapper-contract/zapper-base-vector.sol deleted file mode 100644 index 960597a06..000000000 --- a/contracts/zapper-contract/zapper-base-vector.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.6.7; - -import "../lib/safe-math.sol"; -import "../lib/erc20.sol"; -import "../interfaces/pangolin.sol"; -import "../interfaces/joe.sol"; -import "../lib/square-root.sol"; -import "../interfaces/wavax.sol"; -import "../interfaces/globe.sol"; -import "../interfaces/uniAmm.sol"; - -abstract contract ZapperBaseVector { - using SafeERC20 for IERC20; - using Address for address; - using SafeMath for uint256; - using SafeERC20 for IGlobe; - - address public router; - - address public constant wavax = 0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7; - address public constant xptp = 0x060556209E507d30f2167a101bFC6D256Ed2f3e1; - address public constant ptp = 0x22d4002028f537599bE9f666d1c4Fa138522f9c8; - - uint256 public constant minimumAmount = 1000; - - constructor(address _router) public { - // Safety checks to ensure WAVAX token address - WAVAX(wavax).deposit{value: 0}(); - WAVAX(wavax).withdraw(0); - router = _router; - } - - receive() external payable { - assert(msg.sender == wavax); - } - - function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view virtual returns (uint256 swapAmount); - - //returns DUST - function _returnAssets(address[] memory tokens) internal { - uint256 balance; - for (uint256 i; i < tokens.length; i++) { - balance = IERC20(tokens[i]).balanceOf(address(this)); - if (balance > 0) { - if (tokens[i] == wavax) { - WAVAX(wavax).withdraw(balance); - (bool success, ) = msg.sender.call{value: balance}( - new bytes(0) - ); - require(success, "AVAX transfer failed"); - } else { - IERC20(tokens[i]).safeTransfer(msg.sender, balance); - } - } - } - } - - function _swapAndStake(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) public virtual; - - function zapInAVAXPath(address tokenIn) pure internal returns (address[] memory path) { - if (tokenIn == xptp) { - path = new address[](3); - path[0] = wavax; - path[1] = ptp; - path[2] = tokenIn; - } - else { - path = new address[](2); - path[0] = wavax; - path[1] = tokenIn; - } - } - - 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"); - - address token0 = pair.token0(); - address token1 = pair.token1(); - - bool isInputA = token0 == tokenIn; - require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); - - address[] memory path = zapInAVAXPath(tokenIn); - - uint256 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); - } - } - - // transfers tokens from msg.sender to this contract - function zapIn(address snowglobe, uint256 tokenAmountOutMin, address tokenIn, uint256 tokenInAmount) external { - require(tokenInAmount >= minimumAmount, "Insignificant input amount"); - require(IERC20(tokenIn).allowance(msg.sender, address(this)) >= tokenInAmount, "Input token is not approved"); - - // transfer token - IERC20(tokenIn).safeTransferFrom( - msg.sender, - address(this), - tokenInAmount - ); - _swapAndStake(snowglobe, tokenAmountOutMin, tokenIn); - } - - function zapOutAndSwap(address snowglobe, uint256 withdrawAmount, address desiredToken, uint256 desiredTokenOutMin) public virtual; - - function _removeLiquidity(address pair, address to) internal { - IERC20(pair).safeTransfer(pair, IERC20(pair).balanceOf(address(this))); - (uint256 amount0, uint256 amount1) = IUniPair(pair).burn(to); - - require(amount0 >= minimumAmount, "Router: INSUFFICIENT_A_AMOUNT"); - require(amount1 >= minimumAmount, "Router: INSUFFICIENT_B_AMOUNT"); - } - - function _getVaultPair(address snowglobe) internal view returns (IGlobe vault, IUniPair pair){ - - vault = IGlobe(snowglobe); - pair = IUniPair(vault.token()); - - - - 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) { - IERC20(token).safeApprove(spender, uint256(~0)); - } - } - - function zapOut(address snowglobe, uint256 withdrawAmount) external { - (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); - - 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(); - - _returnAssets(tokens); - } -} \ No newline at end of file diff --git a/contracts/zapper-contract/zapper-base.sol b/contracts/zapper-contract/zapper-base.sol index a0b43126d..8ce089166 100644 --- a/contracts/zapper-contract/zapper-base.sol +++ b/contracts/zapper-contract/zapper-base.sol @@ -21,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 { @@ -37,12 +40,12 @@ abstract contract ZapperBase { function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view virtual returns (uint256 swapAmount); // returns DUST - function _returnAssets(address[] memory tokens) internal { + function _returnAssets(IERC20[] memory tokens) internal { uint256 balance; for (uint256 i; i < tokens.length; i++) { balance = IERC20(tokens[i]).balanceOf(address(this)); if (balance > 0) { - if (tokens[i] == wavax) { + if (tokens[i] == IERC20(wavax)) { WAVAX(wavax).withdraw(balance); (bool success, ) = msg.sender.call{value: balance}( new bytes(0) @@ -57,6 +60,73 @@ abstract contract ZapperBase { function _swapAndStake(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) public virtual; + // 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{ require(msg.value >= minimumAmount, "Insignificant input amount"); @@ -67,6 +137,7 @@ abstract contract ZapperBase { if (tokenIn != wavax){ uint256 _amount = IERC20(wavax).balanceOf(address(this)); + // gets the token pair in the lp (, IUniPair pair) = _getVaultPair(snowglobe); (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); @@ -78,22 +149,10 @@ abstract contract ZapperBase { bool isInputA = token0 == tokenIn; require(isInputA || 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 - ); + // 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); @@ -142,6 +201,8 @@ abstract contract ZapperBase { function zapOut(address snowglobe, uint256 withdrawAmount) external { (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); + address token0 = pair.token0(); + address token1 = pair.token1(); IERC20(snowglobe).safeTransferFrom(msg.sender, address(this), withdrawAmount); vault.withdraw(withdrawAmount); @@ -153,9 +214,9 @@ abstract contract ZapperBase { _removeLiquidity(address(pair), address(this)); - address[] memory tokens = new address[](2); - tokens[0] = pair.token0(); - tokens[1] = pair.token1(); + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = IERC20(token0); + tokens[1] = IERC20(token1); _returnAssets(tokens); } From 6683112382865a0a9d8a27fa96968986d4226e22 Mon Sep 17 00:00:00 2001 From: Abigail Date: Mon, 4 Apr 2022 17:17:38 -0500 Subject: [PATCH 5/6] ready for review --- .../SnowglobeZapperPangolin.sol | 28 +++++++++++++--- .../SnowglobeZapperTraderJoe.sol | 31 +++++++++++++----- .../zapper-contract/SnowglobeZapperVector.sol | 31 +++++++++++++----- contracts/zapper-contract/zapper-base.sol | 32 +++++++++---------- 4 files changed, 85 insertions(+), 37 deletions(-) diff --git a/contracts/zapper-contract/SnowglobeZapperPangolin.sol b/contracts/zapper-contract/SnowglobeZapperPangolin.sol index f712b6fdf..eff1d26c1 100644 --- a/contracts/zapper-contract/SnowglobeZapperPangolin.sol +++ b/contracts/zapper-contract/SnowglobeZapperPangolin.sol @@ -25,7 +25,19 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { address swapToken = token1 == desiredToken ? token0 : token1; _approveTokenIfNeeded(swapToken, address(router)); - (IERC20[] memory path, ) = _swapToken(swapToken, desiredToken, IERC20(swapToken).balanceOf(address(this))); + address[] memory path = new address[](2); + path[0] = swapToken; + path[1] = desiredToken; + + _approveTokenIfNeeded(path[0], address(router)); + IPangolinRouter(router).swapExactTokensForTokens( + IERC20(swapToken).balanceOf(address(this)), + desiredTokenOutMin, + path, + address(this), + block.timestamp + ); + _returnAssets(path); } @@ -55,14 +67,20 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { } _approveTokenIfNeeded(path[0], address(router)); - (IERC20[] memory paths, uint256[] memory amounts) = _swapToken(path[0], path[1] , swapAmountIn); + uint256[] memory swappedAmounts = IPangolinRouter(router).swapExactTokensForTokens( + swapAmountIn, + tokenAmountOutMin, + path, + address(this), + block.timestamp + ); _approveTokenIfNeeded(path[1], address(router)); (, , uint256 amountLiquidity) = IPangolinRouter(router).addLiquidity( path[0], path[1], - fullInvestment.sub(amounts[0]), - amounts[1], + fullInvestment.sub(swappedAmounts[0]), + swappedAmounts[1], 1, 1, address(this), @@ -81,7 +99,7 @@ contract SnowglobeZapAvaxPangolin is ZapperBase { //deposit for into gauge IGaugeV2(gaugeAddress).depositFor(vault.balanceOf(msg.sender), msg.sender); - _returnAssets(paths); + _returnAssets(path); } function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view override returns (uint256 swapAmount) { diff --git a/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol b/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol index 90b1204c9..bfbca11c2 100644 --- a/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol +++ b/contracts/zapper-contract/SnowglobeZapperTraderJoe.sol @@ -24,7 +24,18 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { address swapToken = token1 == desiredToken ? token0 : token1; _approveTokenIfNeeded(swapToken, address(router)); - (IERC20[] memory path, ) = _swapToken(swapToken, desiredToken, IERC20(swapToken).balanceOf(address(this))); + 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); } @@ -54,14 +65,21 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { } _approveTokenIfNeeded(path[0], address(router)); - (IERC20[] memory paths, uint256[] memory amounts) = _swapToken(path[0], path[1] , swapAmountIn); + 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(amounts[0]), - amounts[1], + fullInvestment.sub(swappedAmounts[0]), + swappedAmounts[1], 1, 1, address(this), @@ -71,9 +89,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))); @@ -83,7 +98,7 @@ contract SnowglobeZapAvaxTraderJoe is ZapperBase { //deposit for into gauge IGaugeV2(gaugeAddress).depositFor(vault.balanceOf(msg.sender), msg.sender); - _returnAssets(paths); + _returnAssets(path); } function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view override returns (uint256 swapAmount) { diff --git a/contracts/zapper-contract/SnowglobeZapperVector.sol b/contracts/zapper-contract/SnowglobeZapperVector.sol index de6874ffa..2c6c72331 100644 --- a/contracts/zapper-contract/SnowglobeZapperVector.sol +++ b/contracts/zapper-contract/SnowglobeZapperVector.sol @@ -24,7 +24,18 @@ contract SnowglobeZapAvaxVector is ZapperBase { address swapToken = token1 == desiredToken ? token0 : token1; _approveTokenIfNeeded(swapToken, address(router)); - (IERC20[] memory path, ) = _swapToken(swapToken, desiredToken, IERC20(swapToken).balanceOf(address(this))); + 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); } @@ -54,14 +65,21 @@ contract SnowglobeZapAvaxVector is ZapperBase { } _approveTokenIfNeeded(path[0], address(router)); - (IERC20[] memory paths, uint256[] memory amounts) = _swapToken(path[0], path[1] , swapAmountIn); + 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(amounts[0]), - amounts[1], + fullInvestment.sub(swappedAmounts[0]), + swappedAmounts[1], 1, 1, address(this), @@ -71,9 +89,6 @@ contract SnowglobeZapAvaxVector 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))); @@ -83,7 +98,7 @@ contract SnowglobeZapAvaxVector is ZapperBase { //deposit for into gauge IGaugeV2(gaugeAddress).depositFor(vault.balanceOf(msg.sender), msg.sender); - _returnAssets(paths); + _returnAssets(path); } function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view override returns (uint256 swapAmount) { diff --git a/contracts/zapper-contract/zapper-base.sol b/contracts/zapper-contract/zapper-base.sol index 8ce089166..6d2bdc204 100644 --- a/contracts/zapper-contract/zapper-base.sol +++ b/contracts/zapper-contract/zapper-base.sol @@ -40,12 +40,12 @@ abstract contract ZapperBase { function _getSwapAmount(uint256 investmentA, uint256 reserveA, uint256 reserveB) public view virtual returns (uint256 swapAmount); // returns DUST - function _returnAssets(IERC20[] memory tokens) internal { + function _returnAssets(address[] memory tokens) internal { uint256 balance; for (uint256 i; i < tokens.length; i++) { balance = IERC20(tokens[i]).balanceOf(address(this)); if (balance > 0) { - if (tokens[i] == IERC20(wavax)) { + if (tokens[i] == wavax) { WAVAX(wavax).withdraw(balance); (bool success, ) = msg.sender.call{value: balance}( new bytes(0) @@ -133,21 +133,21 @@ abstract contract ZapperBase { // Get balance of native AVAX and wrap AVAX into ERC20 (WAVAX) 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)); + // gets the token pair in the lp + (, IUniPair pair) = _getVaultPair(snowglobe); - // 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"); - (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 - 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"); - 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 @@ -214,9 +214,9 @@ abstract contract ZapperBase { _removeLiquidity(address(pair), address(this)); - IERC20[] memory tokens = new IERC20[](2); - tokens[0] = IERC20(token0); - tokens[1] = IERC20(token1); + address[] memory tokens = new address[](2); + tokens[0] = token0; + tokens[1] = token1; _returnAssets(tokens); } From 4232220862006e7b8dae0474e679f67aade389e7 Mon Sep 17 00:00:00 2001 From: Abigail Date: Tue, 12 Apr 2022 10:41:04 -0500 Subject: [PATCH 6/6] review ready --- .../SnowglobeZapperPangolin.sol | 93 +++++++++++++++++- .../SnowglobeZapperTraderJoe.sol | 66 ++++++++++++- .../zapper-contract/SnowglobeZapperVector.sol | 65 ++++++++++++- contracts/zapper-contract/zapper-base.sol | 94 ++++++++++--------- 4 files changed, 264 insertions(+), 54 deletions(-) diff --git a/contracts/zapper-contract/SnowglobeZapperPangolin.sol b/contracts/zapper-contract/SnowglobeZapperPangolin.sol index eff1d26c1..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"); @@ -37,13 +94,17 @@ 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"); @@ -122,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"); @@ -139,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 bfbca11c2..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(); @@ -36,10 +93,15 @@ 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); @@ -121,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 index 2c6c72331..733b929cf 100644 --- a/contracts/zapper-contract/SnowglobeZapperVector.sol +++ b/contracts/zapper-contract/SnowglobeZapperVector.sol @@ -12,6 +12,63 @@ contract SnowglobeZapAvaxVector 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(); @@ -40,6 +97,12 @@ contract SnowglobeZapAvaxVector is ZapperBase { _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); @@ -121,7 +184,7 @@ contract SnowglobeZapAvaxVector 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/zapper-base.sol b/contracts/zapper-contract/zapper-base.sol index 6d2bdc204..ede170c38 100644 --- a/contracts/zapper-contract/zapper-base.sol +++ b/contracts/zapper-contract/zapper-base.sol @@ -127,37 +127,39 @@ abstract contract ZapperBase { ); } - function zapInAVAX(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) external payable{ - require(msg.value >= minimumAmount, "Insignificant input amount"); + function zapInAVAX(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) external payable virtual; - // Get balance of native AVAX and wrap AVAX into ERC20 (WAVAX) - WAVAX(wavax).deposit{value: msg.value}(); + // function zapInAVAX(address snowglobe, uint256 tokenAmountOutMin, address tokenIn) external payable{ + // require(msg.value >= minimumAmount, "Insignificant input amount"); - // gets the token pair in the lp - (, IUniPair pair) = _getVaultPair(snowglobe); + // // Get balance of native AVAX and wrap AVAX into ERC20 (WAVAX) + // WAVAX(wavax).deposit{value: msg.value}(); - (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); - require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); + // // gets the token pair in the lp + // (, IUniPair pair) = _getVaultPair(snowglobe); - address token0 = pair.token0(); // first token in the pool - address token1 = pair.token1(); // second token in the pool + // (uint256 reserveA, uint256 reserveB, ) = pair.getReserves(); + // require(reserveA > minimumAmount && reserveB > minimumAmount, "Liquidity pair reserves too low"); - bool isInputA = token0 == tokenIn; - require(isInputA || 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 - // allows us to zapIn if avax isn't part of the original pair - if (tokenIn != wavax){ - uint256 _amount = IERC20(wavax).balanceOf(address(this)); + // bool isInputA = token0 == tokenIn; + // require(isInputA || token1 == tokenIn, "Input token not present in liquidity pair"); - // 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); - } - } + // // 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 { @@ -183,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) { @@ -199,25 +199,27 @@ abstract contract ZapperBase { } } - function zapOut(address snowglobe, uint256 withdrawAmount) external { - (IGlobe vault, IUniPair pair) = _getVaultPair(snowglobe); - address token0 = pair.token0(); - address token1 = pair.token1(); + 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] = token0; - tokens[1] = 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