Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/generic harvester #117

Merged
merged 69 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
4956509
feat: first version of generic harvester
0xtekgrinder Sep 13, 2024
513009b
feat: generic harvester swap and vault
0xtekgrinder Sep 13, 2024
455ea99
feat: remove access control from adjustYield
0xtekgrinder Sep 13, 2024
8b0a95d
feat: merge genericHarvester into a single contract
0xtekgrinder Sep 16, 2024
f620502
feat: multi block rebalancer
0xtekgrinder Sep 17, 2024
875665f
feat: correct permissions for MultiMockRebalancer
0xtekgrinder Sep 17, 2024
857caee
feat: slippage handling for MultiblockRebalancer
0xtekgrinder Sep 17, 2024
919971a
feat: remove old harvesters
0xtekgrinder Sep 20, 2024
cd1e1b4
feat: correct slippage computation for the vault
0xtekgrinder Sep 20, 2024
737b96c
feat: refactory harvesters to abstract common code
0xtekgrinder Sep 20, 2024
b7e1a89
feat: deploy scripts for new harvesters
0xtekgrinder Sep 20, 2024
9afc214
style: format BaseRebalancer
0xtekgrinder Sep 20, 2024
6580fdd
feat: remove comment
0xtekgrinder Sep 20, 2024
c2c6d06
chore: remove useless file
0xtekgrinder Sep 20, 2024
7b96168
fix: correct computation of slippage for XEVT
0xtekgrinder Sep 20, 2024
c3da3ef
feat: specify balance in finalizeRebalance function
0xtekgrinder Sep 20, 2024
88210b9
refactor: rename swap arguments
0xtekgrinder Sep 20, 2024
a4f4b85
refactor: change mapping of stablecoin to yieldbearing to the oposite
0xtekgrinder Sep 23, 2024
00ee738
feat: harvest function for both harvester contracts
0xtekgrinder Sep 23, 2024
6c3da10
feat: checkSlippage with decimals
0xtekgrinder Sep 23, 2024
5454e28
chore: remove old rebalancer interfaces
0xtekgrinder Sep 23, 2024
dfddaa3
style: fix lint issues for the new contracts
0xtekgrinder Sep 23, 2024
ef574fc
feat: remove useless setYieldBearingAssetData function override
0xtekgrinder Sep 29, 2024
4dd5a4f
feat: correct computation of the rebalancing
0xtekgrinder Oct 7, 2024
d8c9aa1
fix: correct sleeping check + toggleTrusted
0xtekgrinder Oct 7, 2024
3eb3da7
TEMP feat: tests
0xtekgrinder Oct 8, 2024
7b0d455
feat: computeRebalanceAmount public function
0xtekgrinder Oct 8, 2024
36d22ea
fix: correct rebalancing computation + min/max for stablecoin
0xtekgrinder Oct 8, 2024
e01866c
tests: add more complete tests for the harvesting of the different as…
0xtekgrinder Oct 8, 2024
3fc3e83
refactor: rename minExposure / maxExposure to be more explicit
0xtekgrinder Oct 8, 2024
378f35f
feat: check for msg.sender instead of receiver when removing budget
0xtekgrinder Oct 15, 2024
35132a8
doc: add some explanation on scale
0xtekgrinder Oct 15, 2024
8abc893
feat: system for allowed addresses to update the target exposure of a…
0xtekgrinder Oct 21, 2024
7c417ff
doc: finalize sentance
0xtekgrinder Oct 21, 2024
3e11a2b
doc: misspell
0xtekgrinder Oct 21, 2024
9a10225
feat: remove underflow checks
0xtekgrinder Oct 21, 2024
427eaf5
feat: make updateLimitExposure permissionless
0xtekgrinder Oct 21, 2024
dea0f6c
feat: internalize scaling of amount
0xtekgrinder Oct 21, 2024
4aa4cdc
feat: only allowed or guardian
0xtekgrinder Oct 21, 2024
0ba6215
fix: update state before sending tokens in addBudget
0xtekgrinder Oct 22, 2024
4b701a8
style: refactor blocks for GenericHarvester
0xtekgrinder Oct 22, 2024
e1c67a7
fix: better slippage handling for vaults
0xtekgrinder Oct 22, 2024
f9e9a5f
feat: IXEVT interface
0xtekgrinder Oct 22, 2024
d473b84
feat: refactor access control so there is only one onlyTrusted mapping
0xtekgrinder Oct 22, 2024
3bc5b0c
style: fix lint of comments
0xtekgrinder Oct 22, 2024
bcda179
tests: update tests to add missing functionnalities
0xtekgrinder Oct 22, 2024
2af51df
feat: remove mint and burn from MultiBlockHarvester
0xtekgrinder Oct 25, 2024
29df833
doc: typo
0xtekgrinder Oct 25, 2024
496dda8
feat: declare depositAddress in the upper scope
0xtekgrinder Oct 25, 2024
cf6b776
feat: simplify rebalance
0xtekgrinder Oct 25, 2024
29b7a5a
feat: specify block fork number
0xtekgrinder Oct 25, 2024
2b28e0f
refactor: rename stablecoin to depositAsset
0xtekgrinder Oct 25, 2024
791763f
feat: add unchecked blocks to _checkSlippage
0xtekgrinder Oct 25, 2024
cddfd4b
refactor: rename depositAsset to asset
0xtekgrinder Oct 25, 2024
7a39fc9
feat: remove MaxOrderAmount check
0xtekgrinder Oct 25, 2024
bfeac9e
tests: remove tooBigAmountIn
0xtekgrinder Oct 25, 2024
8146026
tests: setup of generic harvester + budget
0xtekgrinder Oct 25, 2024
08d8a28
tests: setters
0xtekgrinder Oct 25, 2024
e7fd84f
feat: revert if zero amount for GenericHarvester
0xtekgrinder Oct 25, 2024
2d0c3d2
fix: correct generic harvester swaps
0xtekgrinder Oct 28, 2024
1139a9b
tests: add not enough budget test
0xtekgrinder Oct 28, 2024
094f958
feat: remove swap slippage
0xtekgrinder Oct 28, 2024
bf111b9
tests: increase and decrease steak_USDC exposure
0xtekgrinder Oct 28, 2024
e303ae0
feat: remove swapSlippage variable
0xtekgrinder Oct 28, 2024
0fc36d0
feat: set maxSlippage to 0.3%
0xtekgrinder Oct 29, 2024
c85bfb8
tests: add generic harvester swap test
0xtekgrinder Oct 29, 2024
b05e00c
tests: add decimals tests
0xtekgrinder Oct 29, 2024
671eb76
feat: recover ERC20s
0xtekgrinder Oct 29, 2024
e5d7e51
feat: events for harvester
0xtekgrinder Oct 29, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@

pragma solidity ^0.8.23;

import { SafeCast } from "oz/utils/math/SafeCast.sol";

import { ITransmuter } from "interfaces/ITransmuter.sol";

import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { AccessControl, IAccessControlManager } from "../utils/AccessControl.sol";
import "../utils/Constants.sol";
import "../utils/Errors.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ITransmuter } from "../interfaces/ITransmuter.sol";
import { IAgToken } from "../interfaces/IAgToken.sol";

import { IRebalancerFlashloan } from "../interfaces/IRebalancerFlashloan.sol";
import "../utils/Errors.sol";

struct CollatParams {
// Yield bearing asset associated to the collateral
// Address of the collateral
address asset;
// Target exposure to the collateral asset used
uint64 targetExposure;
Expand All @@ -26,39 +24,84 @@
uint64 overrideExposures;
}

/// @title BaseHarvester
/// @author Angle Labs, Inc.
/// @dev Generic contract for anyone to permissionlessly adjust the reserves of Angle Transmuter through
contract BaseHarvester is AccessControl {
using SafeCast for uint256;
abstract contract BaseRebalancer is AccessControl {
using SafeERC20 for IERC20;

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/

/// @notice Reference to the `transmuter` implementation this contract aims at rebalancing
ITransmuter public immutable TRANSMUTER;
/// @notice Permissioned rebalancer contract
IRebalancerFlashloan public rebalancer;
ITransmuter public immutable transmuter;

Check warning on line 35 in contracts/helpers/BaseRebalancer.sol

View workflow job for this annotation

GitHub Actions / lint

Immutable variables name are set to be in capitalized SNAKE_CASE
/// @notice AgToken handled by the `transmuter` of interest
IAgToken public immutable agToken;

Check warning on line 37 in contracts/helpers/BaseRebalancer.sol

View workflow job for this annotation

GitHub Actions / lint

Immutable variables name are set to be in capitalized SNAKE_CASE
/// @notice Max slippage when dealing with the Transmuter
uint96 public maxSlippage;
/// @notice Data associated to a collateral
mapping(address => CollatParams) public collateralData;

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
INITIALIZATION
CONSTRUCTOR
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/

constructor(
address _rebalancer,
uint96 initialMaxSlippage,
IAccessControlManager definitiveAccessControlManager,
IAgToken definitiveAgToken,
ITransmuter definitiveTransmuter
) {
_setMaxSlippage(initialMaxSlippage);
accessControlManager = definitiveAccessControlManager;
agToken = definitiveAgToken;
transmuter = definitiveTransmuter;
}
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Check failure on line 58 in contracts/helpers/BaseRebalancer.sol

View workflow job for this annotation

GitHub Actions / lint

Insert ⏎
GUARDIAN FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/

/**
* @notice Set the collateral data
* @param collateral address of the collateral
* @param targetExposure target exposure to the collateral asset used
* @param minExposureYieldAsset minimum exposure within the Transmuter to the asset
* @param maxExposureYieldAsset maximum exposure within the Transmuter to the asset
* @param overrideExposures whether limit exposures should be overriden or read onchain through the Transmuter
* This value should be 1 to override exposures or 2 if these shouldn't be overriden
*/
function setCollateralData(
address collateral,
address asset,
uint64 targetExposure,
uint64 overrideExposures,
uint64 minExposureYieldAsset,
uint64 maxExposureYieldAsset,
uint64 overrideExposures
) external onlyGuardian {
_setCollateralData(
collateral,
collateral,
targetExposure,
minExposureYieldAsset,
maxExposureYieldAsset,
overrideExposures
);
}

/**
* @notice Set the collateral data
* @param collateral address of the collateral
* @param asset address of the asset
* @param targetExposure target exposure to the collateral asset used
* @param minExposureYieldAsset minimum exposure within the Transmuter to the asset
* @param maxExposureYieldAsset maximum exposure within the Transmuter to the asset
* @param overrideExposures whether limit exposures should be overriden or read onchain through the Transmuter
*/
function setCollateralData(
address collateral,
address asset,
uint64 targetExposure,
uint64 minExposureYieldAsset,
uint96 _maxSlippage
) {
ITransmuter transmuter = IRebalancerFlashloan(_rebalancer).TRANSMUTER();
TRANSMUTER = transmuter;
rebalancer = IRebalancerFlashloan(_rebalancer);
accessControlManager = IAccessControlManager(transmuter.accessControlManager());
uint64 maxExposureYieldAsset,
uint64 overrideExposures
) external onlyGuardian {
_setCollateralData(
collateral,
asset,
Expand All @@ -67,26 +110,35 @@
maxExposureYieldAsset,
overrideExposures
);
_setMaxSlippage(_maxSlippage);
}

/**
* @notice Set the limit exposures to the yield bearing asset
* @param collateral address of the collateral
*/
function updateLimitExposuresYieldAsset(address collateral) public virtual onlyGuardian {
CollatParams storage collatInfo = collateralData[collateral];
if (collatInfo.overrideExposures == 2) _updateLimitExposuresYieldAsset(collatInfo);
}

/**
* @notice Set the max allowed slippage
* @param newMaxSlippage new max allowed slippage
*/
function setMaxSlippage(uint96 newMaxSlippage) external onlyGuardian {
_setMaxSlippage(newMaxSlippage);
}

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HARVEST
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/

/// @notice Invests or divests from the yield asset associated to `collateral` based on the current exposure to this
/// collateral
/// @dev This transaction either reduces the exposure to `collateral` in the Transmuter or frees up some collateral
/// that can then be used for people looking to burn stablecoins
/// @dev Due to potential transaction fees within the Transmuter, this function doesn't exactly bring `collateral`
/// to the target exposure
function harvest(address collateral, uint256 scale, bytes calldata extraData) public virtual {
if (scale > 1e9) revert InvalidParam();
(uint256 stablecoinsFromCollateral, uint256 stablecoinsIssued) = TRANSMUTER.getIssuedByCollateral(collateral);
CollatParams memory collatInfo = collateralData[collateral];
(uint256 stablecoinsFromAsset, ) = TRANSMUTER.getIssuedByCollateral(collatInfo.asset);
uint8 increase;
uint256 amount;
function _computeRebalanceAmount(
address collateral,
CollatParams memory collatInfo
) internal view returns (uint8 increase, uint256 amount) {
(uint256 stablecoinsFromCollateral, uint256 stablecoinsIssued) = transmuter.getIssuedByCollateral(collateral);
(uint256 stablecoinsFromAsset, ) = transmuter.getIssuedByCollateral(collatInfo.asset);
uint256 targetExposureScaled = collatInfo.targetExposure * stablecoinsIssued;
if (stablecoinsFromCollateral * 1e9 > targetExposureScaled) {
// Need to increase exposure to yield bearing asset
Expand All @@ -107,60 +159,6 @@
else if (stablecoinsFromAsset * 1e9 < minValueScaled + amount * 1e9)
amount = stablecoinsFromAsset - minValueScaled / 1e9;
}
amount = (amount * scale) / 1e9;
if (amount > 0) {
try TRANSMUTER.updateOracle(collatInfo.asset) {} catch {}

rebalancer.adjustYieldExposure(
amount,
increase,
collateral,
collatInfo.asset,
(amount * (1e9 - maxSlippage)) / 1e9,
extraData
);
}
}

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SETTERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/

function setRebalancer(address _newRebalancer) public virtual onlyGuardian {
if (_newRebalancer == address(0)) revert ZeroAddress();
rebalancer = IRebalancerFlashloan(_newRebalancer);
}

function setCollateralData(
address collateral,
address asset,
uint64 targetExposure,
uint64 minExposureYieldAsset,
uint64 maxExposureYieldAsset,
uint64 overrideExposures
) public virtual onlyGuardian {
_setCollateralData(
collateral,
asset,
targetExposure,
minExposureYieldAsset,
maxExposureYieldAsset,
overrideExposures
);
}

function setMaxSlippage(uint96 _maxSlippage) public virtual onlyGuardian {
_setMaxSlippage(_maxSlippage);
}

function updateLimitExposuresYieldAsset(address collateral) public virtual {
CollatParams storage collatInfo = collateralData[collateral];
if (collatInfo.overrideExposures == 2) _updateLimitExposuresYieldAsset(collatInfo);
}

function _setMaxSlippage(uint96 _maxSlippage) internal virtual {
if (_maxSlippage > 1e9) revert InvalidParam();
maxSlippage = _maxSlippage;
}

function _setCollateralData(
0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -188,15 +186,29 @@

function _updateLimitExposuresYieldAsset(CollatParams storage collatInfo) internal virtual {
uint64[] memory xFeeMint;
(xFeeMint, ) = TRANSMUTER.getCollateralMintFees(collatInfo.asset);
(xFeeMint, ) = transmuter.getCollateralMintFees(collatInfo.asset);
uint256 length = xFeeMint.length;
if (length <= 1) collatInfo.maxExposureYieldAsset = 1e9;
else collatInfo.maxExposureYieldAsset = xFeeMint[length - 2];

uint64[] memory xFeeBurn;
(xFeeBurn, ) = TRANSMUTER.getCollateralBurnFees(collatInfo.asset);
(xFeeBurn, ) = transmuter.getCollateralBurnFees(collatInfo.asset);
length = xFeeBurn.length;
if (length <= 1) collatInfo.minExposureYieldAsset = 0;
else collatInfo.minExposureYieldAsset = xFeeBurn[length - 2];
}

function _setMaxSlippage(uint96 newMaxSlippage) internal virtual {
if (newMaxSlippage > 1e9) revert InvalidParam();
maxSlippage = newMaxSlippage;
}

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HELPER
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/

function _adjustAllowance(address token, address sender, uint256 amountIn) internal {
uint256 allowance = IERC20(token).allowance(address(this), sender);
if (allowance < amountIn) IERC20(token).safeIncreaseAllowance(sender, type(uint256).max - allowance);
}
}
113 changes: 0 additions & 113 deletions contracts/helpers/BaseRebalancerFlashloan.sol

This file was deleted.

Loading
Loading