Skip to content

Commit

Permalink
Add peapods strategies using UniV2 and Balancer Lp compounder
Browse files Browse the repository at this point in the history
  • Loading branch information
Andreadinenno committed Jun 10, 2024
1 parent 5ebdc60 commit 6a6de72
Show file tree
Hide file tree
Showing 11 changed files with 1,222 additions and 0 deletions.
124 changes: 124 additions & 0 deletions src/peripheral/BaseUniV2Compounder.sol
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);
}
}
79 changes: 79 additions & 0 deletions src/peripheral/BaseUniV2LpCompounder.sol
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;
}
}
42 changes: 42 additions & 0 deletions src/peripheral/UniswapV2TradeLibrary.sol
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
);
}
}
84 changes: 84 additions & 0 deletions src/strategies/peapods/IPeapods.sol
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;
}
Loading

0 comments on commit 6a6de72

Please sign in to comment.