Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions contracts/strategies/arbitrum/level/CompoundingLVL.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/IReservedLevelOmniStaking.sol";
import "./interfaces/ILevelPool.sol";
import "./interfaces/ILevelLiquidityCalculator.sol";
import "./interfaces/ILevelOracle.sol";

contract CompoundingLVL is BaseStrategy {
IReservedLevelOmniStaking public immutable levelStaking;
ILevelPool public immutable levelPool;
ILevelLiquidityCalculator public immutable liquidityCalculator;
ILevelOracle public immutable levelOracle;

constructor(
address _reservedLevelOmniStaking,
address _levelLiquidityPool,
BaseStrategySettings memory _baseStrategySettings,
StrategySettings memory _strategySettings
) BaseStrategy(_baseStrategySettings, _strategySettings) {
levelStaking = IReservedLevelOmniStaking(_reservedLevelOmniStaking);
levelPool = ILevelPool(_levelLiquidityPool);
liquidityCalculator = ILevelLiquidityCalculator(levelPool.liquidityCalculator());
levelOracle = ILevelOracle(levelPool.oracle());
}

function _depositToStakingContract(uint256 _amount, uint256) internal override {
depositToken.approve(address(levelStaking), _amount);
levelStaking.stake(address(this), _amount);
}

function _getDepositFeeBips() internal view override returns (uint256) {
return levelStaking.STAKING_TAX();
}

function _bip() internal view override returns (uint256) {
return levelStaking.STAKING_TAX_PRECISION();
}

function _withdrawFromStakingContract(uint256 _amount) internal override returns (uint256 _withdrawAmount) {
levelStaking.unstake(address(this), _amount);
_withdrawAmount = _amount;
}

function _emergencyWithdraw() internal override {
levelStaking.unstake(address(this), totalDeposits());
depositToken.approve(address(levelStaking), 0);
}

function _pendingRewards() internal view override returns (Reward[] memory) {
uint256 pendingLLP = levelStaking.pendingRewards(levelStaking.currentEpoch() - 1, address(this));
if (pendingLLP > 0) {
Reward[] memory pendingRewards = new Reward[](1);
pendingRewards[0].reward = _lowestFeeAsset(pendingLLP);
(pendingRewards[0].amount,) =
liquidityCalculator.calcRemoveLiquidity(levelStaking.LLP(), pendingRewards[0].reward, pendingLLP);
return pendingRewards;
}
return new Reward[](0);
}

function _lowestFeeAsset(uint256 _lpAmount) internal view returns (address lowestFeeAsset) {
(address[] memory assets,) = levelPool.getAllAssets();
uint256 lowestFee = type(uint256).max;
for (uint256 i; i < assets.length; i++) {
if (levelStaking.claimableTokens(assets[i])) {
uint256 price = levelOracle.getPrice(assets[i], true);
uint256 fee = liquidityCalculator.calcAddRemoveLiquidityFee(assets[i], price, _lpAmount, false);
if (fee < lowestFee) {
lowestFee = fee;
lowestFeeAsset = assets[i];
}
}
}
}

function _getRewards() internal override {
Reward[] memory pendingRewards = _pendingRewards();
if (pendingRewards.length > 0 && pendingRewards[0].amount > 0) {
levelStaking.claimRewardsToSingleToken(
levelStaking.currentEpoch() - 1, address(this), pendingRewards[0].reward, 0
);
}
}

function totalDeposits() public view override returns (uint256) {
return levelStaking.stakedAmounts(address(this));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface ILevelLiquidityCalculator {
function calcRemoveLiquidity(address _tranche, address _tokenOut, uint256 _lpAmount)
external
view
returns (uint256 outAmountAfterFee, uint256 feeAmount);

function calcAddRemoveLiquidityFee(address _token, uint256 _tokenPrice, uint256 _valueChange, bool _isAdd)
external
view
returns (uint256);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface ILevelOracle {
function getPrice(address token, bool max) external view returns (uint256);
}
6 changes: 6 additions & 0 deletions contracts/strategies/arbitrum/level/interfaces/ILevelPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ pragma solidity 0.8.13;
interface ILevelPool {
function addLiquidity(address tranche, address assetToken, uint256 assetAmount, uint256 minLpAmount, address to)
external;

function liquidityCalculator() external view returns (address);

function oracle() external view returns (address);

function getAllAssets() external view returns (address[] memory, bool[] memory);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

interface IReservedLevelOmniStaking {
struct UserInfo {
/// @notice staked amount of user in epoch
uint256 amount;
uint256 claimedReward;
/// @notice accumulated amount, calculated by total of deposited amount multiplied with deposited time
uint256 accShare;
uint256 lastUpdateAccShareTime;
}

function currentEpoch() external view returns (uint256);

function pendingRewards(uint256 _epoch, address _user) external view returns (uint256 _pendingRewards);

function stake(address _to, uint256 _amount) external;

function unstake(address _to, uint256 _amount) external;

function stakedAmounts(address _user) external view returns (uint256);

function claimRewards(uint256 _epoch, address _to) external;

function claimRewardsToSingleToken(uint256 _epoch, address _to, address _tokenOut, uint256 _minAmountOut)
external;

function STAKING_TAX_PRECISION() external view returns (uint256);

function STAKING_TAX() external view returns (uint256);

function claimableTokens(address _token) external view returns (bool);

function LLP() external view returns (address);
}