-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add peapods strategies using UniV2 and Balancer Lp compounder
- Loading branch information
1 parent
5ebdc60
commit 6a6de72
Showing
11 changed files
with
1,222 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
// Docgen-SOLC: 0.8.25 | ||
|
||
pragma solidity ^0.8.25; | ||
|
||
import {UniswapV2TradeLibrary, IUniswapRouterV2} from "./UniswapV2TradeLibrary.sol"; | ||
import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; | ||
import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import {Math} from "openzeppelin-contracts/utils/math/Math.sol"; | ||
|
||
struct SwapStep { | ||
address[] path; | ||
} | ||
|
||
abstract contract BaseUniV2Compounder { | ||
using SafeERC20 for IERC20; | ||
using Math for uint256; | ||
|
||
IUniswapRouterV2 public uniswapRouter; | ||
|
||
address[] public sellTokens; | ||
SwapStep[] internal sellSwaps; // for each sellToken there are two paths. | ||
|
||
// sell half the rewards for a pool tokenA and half for the tokenB | ||
function sellRewardsForBaseTokensViaUniswapV2() internal { | ||
// caching | ||
IUniswapRouterV2 router = uniswapRouter; | ||
SwapStep memory swap; | ||
|
||
uint256 rewLen = sellTokens.length; | ||
for (uint256 i = 0; i < rewLen;) { | ||
uint256 totAmount = IERC20(sellTokens[i]).balanceOf(address(this)); | ||
|
||
// sell half for tokenA | ||
swap = sellSwaps[2 * i]; | ||
uint256 amount = totAmount.mulDiv(1e18, 2e18, Math.Rounding.Floor); | ||
if (amount > 0) { | ||
UniswapV2TradeLibrary.trade(router, swap.path, address(this), block.timestamp, amount, 0); | ||
} | ||
|
||
// sell the rest for tokenB | ||
swap = sellSwaps[2 * i + 1]; | ||
amount = totAmount - amount; | ||
if (amount > 0) { | ||
UniswapV2TradeLibrary.trade(router, swap.path, address(this), block.timestamp, amount, 0); | ||
} | ||
|
||
unchecked { | ||
++i; | ||
} | ||
} | ||
} | ||
|
||
// sell all rewards for a single token | ||
function sellRewardsViaUniswapV2() internal { | ||
IUniswapRouterV2 router = uniswapRouter; | ||
SwapStep memory swap; | ||
|
||
uint256 rewLen = sellTokens.length; | ||
for (uint256 i = 0; i < rewLen;) { | ||
uint256 totAmount = IERC20(sellTokens[i]).balanceOf(address(this)); | ||
swap = sellSwaps[i]; | ||
|
||
if (totAmount > 0) { | ||
UniswapV2TradeLibrary.trade(router, swap.path, address(this), block.timestamp, totAmount, 0); | ||
} | ||
|
||
unchecked { | ||
++i; | ||
} | ||
} | ||
} | ||
|
||
function setUniswapTradeValues( | ||
address newRouter, | ||
address[] memory rewTokens, | ||
SwapStep[] memory newSwaps | ||
) internal { | ||
// Remove old rewardToken allowance | ||
uint256 sellTokensLen = sellTokens.length; | ||
if (sellTokensLen > 0) { | ||
// caching | ||
address oldRouter = address(uniswapRouter); | ||
address[] memory oldSellTokens = sellTokens; | ||
|
||
// void approvals | ||
for (uint256 i = 0; i < sellTokensLen;) { | ||
IERC20(oldSellTokens[i]).forceApprove(oldRouter, 0); | ||
|
||
unchecked { | ||
++i; | ||
} | ||
} | ||
} | ||
|
||
// delete old state | ||
delete sellTokens; | ||
delete sellSwaps; | ||
|
||
// Add new allowance + state | ||
address newRewardToken; | ||
sellTokensLen = rewTokens.length; | ||
for (uint256 i = 0; i < sellTokensLen;) { | ||
newRewardToken = rewTokens[i]; | ||
|
||
IERC20(newRewardToken).forceApprove(newRouter, type(uint256).max); | ||
|
||
sellTokens.push(newRewardToken); | ||
|
||
unchecked { | ||
++i; | ||
} | ||
} | ||
|
||
for (uint256 i=0; i<newSwaps.length;) { | ||
sellSwaps.push(newSwaps[i]); | ||
unchecked { | ||
++i; | ||
} | ||
} | ||
|
||
uniswapRouter = IUniswapRouterV2(newRouter); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
// Docgen-SOLC: 0.8.25 | ||
|
||
pragma solidity ^0.8.25; | ||
|
||
import {BaseUniV2Compounder, UniswapV2TradeLibrary, SwapStep} from "./BaseUniV2Compounder.sol"; | ||
import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; | ||
import {SafeERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; | ||
|
||
abstract contract BaseUniV2LpCompounder is BaseUniV2Compounder { | ||
using SafeERC20 for IERC20; | ||
|
||
address[2] public depositAssets; | ||
int128 public indexIn; | ||
|
||
error CompoundFailed(); | ||
|
||
function sellRewardsForLpTokenViaUniswap( | ||
address vaultAsset, | ||
address to, | ||
uint deadline, | ||
bytes memory data | ||
) | ||
internal | ||
{ | ||
sellRewardsForBaseTokensViaUniswapV2(); | ||
|
||
address tokenA = depositAssets[0]; | ||
address tokenB = depositAssets[1]; | ||
|
||
uint256 amountA = IERC20(tokenA).balanceOf(address(this)); | ||
uint256 amountB = IERC20(tokenB).balanceOf(address(this)); | ||
|
||
if(amountA > 0 && amountB > 0) { | ||
UniswapV2TradeLibrary.addLiquidity( | ||
uniswapRouter, | ||
depositAssets[0], | ||
depositAssets[1], | ||
amountA, | ||
amountB, | ||
0, | ||
0, | ||
to, | ||
deadline | ||
); | ||
|
||
uint256 amountLP = IERC20(vaultAsset).balanceOf(address(this)); | ||
uint256 minOut = abi.decode(data, (uint256)); | ||
if (amountLP < minOut) revert CompoundFailed(); | ||
} | ||
} | ||
|
||
function setUniswapLpCompounderValues( | ||
address newRouter, | ||
address[2] memory newDepositAssets, | ||
address[] memory rewardTokens, | ||
SwapStep[] memory newSwaps | ||
) internal { | ||
setUniswapTradeValues(newRouter, rewardTokens, newSwaps); | ||
|
||
address tokenA = newDepositAssets[0]; | ||
address tokenB = newDepositAssets[1]; | ||
|
||
address oldTokenA = depositAssets[0]; | ||
address oldTokenB = depositAssets[1]; | ||
|
||
if (oldTokenA != address(0)) { | ||
IERC20(oldTokenA).forceApprove(address(uniswapRouter), 0); | ||
} | ||
if (oldTokenB != address(0)) { | ||
IERC20(oldTokenB).forceApprove(address(uniswapRouter), 0); | ||
} | ||
|
||
IERC20(tokenA).forceApprove(address(uniswapRouter), type(uint256).max); | ||
IERC20(tokenB).forceApprove(address(uniswapRouter), type(uint256).max); | ||
|
||
depositAssets = newDepositAssets; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
// Docgen-SOLC: 0.8.25 | ||
|
||
pragma solidity ^0.8.25; | ||
|
||
import {IUniswapRouterV2} from "../interfaces/external/uni/IUniswapRouterV2.sol"; | ||
|
||
library UniswapV2TradeLibrary { | ||
function trade( | ||
IUniswapRouterV2 router, | ||
address[] memory path, | ||
address receiver, | ||
uint256 deadline, | ||
uint256 amount, | ||
uint256 minOut | ||
) internal returns (uint256[] memory amounts) { | ||
amounts = router.swapExactTokensForTokens(amount, minOut, path, receiver, deadline); | ||
} | ||
|
||
function addLiquidity( | ||
IUniswapRouterV2 router, | ||
address tokenA, | ||
address tokenB, | ||
uint256 amountA, | ||
uint256 amountB, | ||
uint256 amountAMin, | ||
uint256 amountBMin, | ||
address receiver, | ||
uint256 deadline | ||
) internal returns (uint256 amountOutA, uint256 amountOutB, uint256 lpAmountOut) { | ||
(amountOutA, amountOutB, lpAmountOut) = router.addLiquidity( | ||
tokenA, | ||
tokenB, | ||
amountA, | ||
amountB, | ||
amountAMin, | ||
amountBMin, | ||
receiver, | ||
deadline | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// SPDX-FileCopyrightText: 2020 Lido <[email protected]> | ||
|
||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity ^0.8.25; | ||
|
||
import {IERC20} from "openzeppelin-contracts/interfaces/IERC20.sol"; | ||
|
||
// @dev router contract | ||
interface IIndexUtils { | ||
function addLPAndStake( | ||
address _indexFund, | ||
uint256 _amountIdxTokens, | ||
address _pairedLpTokenProvided, | ||
uint256 _amtPairedLpTokenProvided, | ||
uint256 _amountPairedLpTokenMin, | ||
uint256 _slippage, | ||
uint256 _deadline | ||
) external; | ||
|
||
function unstakeAndRemoveLP( | ||
address _indexFund, | ||
uint256 _amountStakedTokens, | ||
uint256 _minLPTokens, | ||
uint256 _minPairedLpToken, | ||
uint256 _deadline | ||
) external; | ||
} | ||
|
||
interface IIndexToken { | ||
function indexTokens(uint256 index) external view returns( | ||
address token, | ||
uint256 weighting, | ||
uint256 basePriceUSDX96, | ||
uint256 c1, | ||
uint256 q1 | ||
); | ||
} | ||
|
||
interface ICamelotLPToken { | ||
function addLiquidityV2(uint256 _idxLPTokens,uint256 _pairedLPTokens,uint256 _slippage,uint256 _deadline) external; | ||
} | ||
|
||
interface ITokenPod { | ||
// unwraps pod-token into its underlying | ||
// _token: underlying address | ||
// _amount: amount of pod-token to unwrap | ||
// _percentage: 100 | ||
function debond(uint256 _amount,address[] memory _token,uint8[] memory _percentage) external; | ||
} | ||
|
||
interface IStakedToken { | ||
// returns the address of the camelotLP token staked | ||
function stakingToken() external view returns (address lpToken); | ||
|
||
// returns the address of the pool to claim rewards | ||
function poolRewards() external view returns (address poolReward); | ||
|
||
// stakes LP token for rewards. output amount is 1:1 | ||
function stake(address user, uint256 amount) external; | ||
|
||
// unstakes and receives LP token, 1:1 | ||
function unstake(uint256 amount) external; | ||
|
||
} | ||
|
||
interface IPoolRewards { | ||
// returns token address | ||
function rewardsToken() external view returns (address); | ||
|
||
function claimReward(address wallet) external; | ||
|
||
function shares(address who) external view returns (uint256); | ||
} | ||
|
||
interface IPeapods { | ||
function totalSupply() external view returns (uint256); | ||
|
||
function getTotalShares() external view returns (uint256); | ||
|
||
function asset() external view returns (address); | ||
|
||
function initialize(bytes memory adapterInitData, address _wethAddress, bytes memory lidoInitData) external; | ||
} |
Oops, something went wrong.