Skip to content

Commit

Permalink
add BoostedPendleStrategy (#235)
Browse files Browse the repository at this point in the history
  • Loading branch information
midnight-commit authored May 5, 2024
1 parent 56d7476 commit efda155
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 4 deletions.
92 changes: 92 additions & 0 deletions contracts/strategies/crosschain/lamapay/LamaPayStrategyBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "./../../BaseStrategy.sol";

import "./../../../interfaces/ILamaPay.sol";

abstract contract LamaPayStrategyBase is BaseStrategy {
using SafeERC20 for IERC20;

struct Stream {
address lamaPayInstance;
address payer;
uint216 amountPerSec;
address token;
}

Stream[] public streams;

event AddStream(address lamaPayInstance, address payer, uint216 amountPerSec, address token);
event RemoveStream(address lamaPayInstance, address payer, uint216 amountPerSec, address token);
event BorkedStream(address lamaPayInstance, address payer, uint216 amountPerSec, address token);

constructor(BaseStrategySettings memory _baseStrategySettings, StrategySettings memory _strategySettings)
BaseStrategy(_baseStrategySettings, _strategySettings)
{}

function addStream(address _lamaPayInstance, address _payer, uint216 _amountPerSec) external onlyDev {
if (_lamaPayInstance > address(0) && _payer > address(0) && _amountPerSec > 0) {
(, bool found) = findStream(_lamaPayInstance, _payer, _amountPerSec);
require(!found, "MemeRushStrategy::Stream already configured!");
address token = ILamaPay(_lamaPayInstance).token();
streams.push(
Stream({lamaPayInstance: _lamaPayInstance, payer: _payer, amountPerSec: _amountPerSec, token: token})
);
emit AddStream(_lamaPayInstance, _payer, _amountPerSec, token);
}
}

function removeStream(address _lamaPayInstance, address _payer, uint216 _amountPerSec) external onlyDev {
(uint256 index, bool found) = findStream(_lamaPayInstance, _payer, _amountPerSec);
require(found, "MemeRushStrategy::Stream not configured!");
streams[index] = streams[streams.length - 1];
streams.pop();
emit RemoveStream(_lamaPayInstance, _payer, _amountPerSec, ILamaPay(_lamaPayInstance).token());
}

function findStream(address _lamaPayInstance, address _payer, uint216 _amountPerSec)
internal
view
returns (uint256 index, bool found)
{
for (uint256 i = 0; i < streams.length; i++) {
if (
_lamaPayInstance == streams[i].lamaPayInstance && _payer == streams[i].payer
&& _amountPerSec == streams[i].amountPerSec
) {
found = true;
index = i;
}
}
}

function _readStream(Stream memory _stream) internal view returns (Reward memory) {
try ILamaPay(_stream.lamaPayInstance).withdrawable(_stream.payer, address(this), _stream.amountPerSec) returns (
uint256 withdrawableAmount, uint256, uint256
) {
return Reward({reward: _stream.token, amount: withdrawableAmount});
} catch {
return Reward({reward: _stream.token, amount: 0});
}
}

function _pendingRewards() internal view virtual override returns (Reward[] memory) {
Reward[] memory rewards = new Reward[](streams.length);
for (uint256 i = 0; i < streams.length; i++) {
rewards[i] = _readStream(streams[i]);
}
return rewards;
}

function _getRewards() internal virtual override {
for (uint256 i = 0; i < streams.length; i++) {
try ILamaPay(streams[i].lamaPayInstance).withdraw(streams[i].payer, address(this), streams[i].amountPerSec)
{} catch {
emit BorkedStream(
streams[i].lamaPayInstance, streams[i].payer, streams[i].amountPerSec, streams[i].token
);
}
}
}
}
99 changes: 99 additions & 0 deletions contracts/strategies/crosschain/pendle/BoostedPendleStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "./../lamapay/LamaPayStrategyBase.sol";

import "./interfaces/IPendleProxy.sol";
import "./interfaces/IPendleRouter.sol";
import "./interfaces/IPendleMarketLP.sol";
import "./interfaces/ISy.sol";

contract BoostedPendleStrategy is LamaPayStrategyBase {
using SafeERC20 for IERC20;

IPendleProxy public proxy;

address public immutable lpTokenIn;
IPendleRouter public immutable pendleRouter;

// EmptySwap means no swap aggregator is involved
IPendleRouter.SwapData private emptySwap;
// EmptyLimit means no limit order is involved
IPendleRouter.LimitOrderData private emptyLimit;
// DefaultApprox means no off-chain preparation is involved, more gas consuming (~ 180k gas)
IPendleRouter.ApproxParams private defaultApprox = IPendleRouter.ApproxParams(0, type(uint256).max, 0, 256, 1e14);

constructor(
address _proxy,
address _pendleRouter,
BaseStrategySettings memory baseStrategySettings,
StrategySettings memory _strategySettings
) LamaPayStrategyBase(baseStrategySettings, _strategySettings) {
proxy = IPendleProxy(_proxy);
pendleRouter = IPendleRouter(_pendleRouter);
(address sy,,) = IPendleMarketLP(address(depositToken)).readTokens();
lpTokenIn = ISy(sy).yieldToken();
}

function setProxy(address _proxy) external onlyDev {
proxy = IPendleProxy(_proxy);
}

function _depositToStakingContract(uint256 _amount, uint256) internal override {
depositToken.transfer(proxy.voter(), _amount);
}

function _withdrawFromStakingContract(uint256 _amount) internal override returns (uint256 _withdrawAmount) {
proxy.withdrawFromStakingContract(address(depositToken), _amount);
return _amount;
}

function _pendingRewards() internal view override returns (Reward[] memory) {
Reward[] memory rewardsProxy = proxy.pendingRewards(address(depositToken));

uint256 length = rewardsProxy.length + streams.length;

Reward[] memory rewards = new Reward[](length);
uint256 i;
for (i; i < streams.length; i++) {
rewards[i] = _readStream(streams[i]);
}
for (uint256 j; j < rewardsProxy.length; j++) {
rewards[i + j] = rewardsProxy[j];
}
return rewards;
}

function _getRewards() internal override {
super._getRewards();
proxy.getRewards(address(depositToken));
}

function _convertRewardTokenToDepositToken(uint256 _fromAmount) internal override returns (uint256 out) {
if (address(rewardToken) != lpTokenIn) {
FormattedOffer memory offer = simpleRouter.query(_fromAmount, address(rewardToken), lpTokenIn);
_fromAmount = _swap(offer);
}
if (_fromAmount > 0) {
IERC20(lpTokenIn).approve(address(pendleRouter), _fromAmount);
IPendleRouter.TokenInput memory tokenInput = IPendleRouter.TokenInput({
tokenIn: lpTokenIn,
netTokenIn: _fromAmount,
tokenMintSy: lpTokenIn,
pendleSwap: address(0),
swapData: emptySwap
});
(out,,) = pendleRouter.addLiquiditySingleToken(
address(this), address(depositToken), 0, defaultApprox, tokenInput, emptyLimit
);
}
}

function totalDeposits() public view override returns (uint256) {
return proxy.totalDeposits(address(depositToken));
}

function _emergencyWithdraw() internal override {
proxy.emergencyWithdraw(address(depositToken));
}
}
2 changes: 1 addition & 1 deletion contracts/strategies/crosschain/pendle/PendleProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ contract PendleProxy {
uint256 amount = IERC20(rewardTokens[i]).balanceOf(address(voter));

uint256 boostFee = _calculateBoostFee(rewardTokens[i], amount);
if (rewardTokens[i] == PENDLE) {
if (rewardTokens[i] == PENDLE && boostFee > 0 && boostFeeReceiver > address(0)) {
voter.safeExecute(
rewardTokens[i], 0, abi.encodeWithSelector(IERC20.transfer.selector, boostFeeReceiver, boostFee)
);
Expand Down
6 changes: 3 additions & 3 deletions contracts/strategies/crosschain/pendle/PendleStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ contract PendleStrategy is BaseStrategy {
IPendleRouter public immutable pendleRouter;

// EmptySwap means no swap aggregator is involved
IPendleRouter.SwapData public emptySwap;
IPendleRouter.SwapData private emptySwap;
// EmptyLimit means no limit order is involved
IPendleRouter.LimitOrderData public emptyLimit;
IPendleRouter.LimitOrderData private emptyLimit;
// DefaultApprox means no off-chain preparation is involved, more gas consuming (~ 180k gas)
IPendleRouter.ApproxParams public defaultApprox = IPendleRouter.ApproxParams(0, type(uint256).max, 0, 256, 1e14);
IPendleRouter.ApproxParams private defaultApprox = IPendleRouter.ApproxParams(0, type(uint256).max, 0, 256, 1e14);

constructor(
address _proxy,
Expand Down

0 comments on commit efda155

Please sign in to comment.