-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: migrate asset manager contract (#160)
* chore: migrate asset manager contract * fix: bridges folder structure * fix: swapper folder structure * fix: config engine test structure * fix: type and unused import * docs: fix author
- Loading branch information
1 parent
78e58db
commit fb9c5de
Showing
32 changed files
with
1,849 additions
and
48 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
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
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,14 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import {OwnableWithGuardian} from 'solidity-utils/contracts/access-control/OwnableWithGuardian.sol'; | ||
|
||
/// @author Llama | ||
abstract contract Common is OwnableWithGuardian { | ||
/// @notice Provided address is zero address | ||
error InvalidZeroAddress(); | ||
|
||
/// @notice One week, in seconds. Vote-locking is rounded down to weeks. | ||
uint256 internal constant WEEK = 7 days; | ||
} |
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,41 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import {ILiquidityGaugeController} from './interfaces/ILiquidityGaugeController.sol'; | ||
import {Common} from './Common.sol'; | ||
|
||
/// @author Llama | ||
abstract contract LSDLiquidityGaugeManager is Common { | ||
event GaugeControllerChanged(address indexed oldController, address indexed newController); | ||
event GaugeVote(address indexed gauge, uint256 amount); | ||
|
||
/// @notice Setting to the same controller address as currently set. | ||
error SameController(); | ||
|
||
/// @notice Address of LSD Gauge Controller | ||
address public gaugeControllerBalancer; | ||
|
||
/// @notice Set the gauge controller used for gauge weight voting | ||
/// @param _gaugeController The gauge controller address | ||
function setGaugeController(address _gaugeController) public onlyOwnerOrGuardian { | ||
if (_gaugeController == address(0)) revert InvalidZeroAddress(); | ||
|
||
address oldController = gaugeControllerBalancer; | ||
if (oldController == _gaugeController) revert SameController(); | ||
|
||
gaugeControllerBalancer = _gaugeController; | ||
|
||
emit GaugeControllerChanged(oldController, gaugeControllerBalancer); | ||
} | ||
|
||
/// @notice Vote for a gauge's weight | ||
/// @param gauge the address of the gauge to vote for | ||
/// @param weight the weight of gaugeAddress in basis points [0, 10.000] | ||
function voteForGaugeWeight(address gauge, uint256 weight) external onlyOwnerOrGuardian { | ||
if (gauge == address(0)) revert InvalidZeroAddress(); | ||
|
||
ILiquidityGaugeController(gaugeControllerBalancer).vote_for_gauge_weights(gauge, weight); | ||
emit GaugeVote(gauge, weight); | ||
} | ||
} |
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,171 @@ | ||
# Strategic Assets Manager Information | ||
|
||
## Main Idea | ||
|
||
The main idea of this new contract is for the DAO to be able to handle strategic assets (assets that will earn rewards, or assets that can be used to | ||
vote on different protocols, for example) without the need to go through the full governance flow every week. | ||
|
||
Take the following example: the DAO wants to hold veBAL as part of its strategy for the long term. To get the most out of veBAL, the DAO should "re-lock" | ||
its holdings on a weekly basis. This is a very tedious approach and can lead to a lot of voter fatigue. Because of this, the StrategicAssetsManager contract | ||
can be governed by the DAO, while some functions can be invoked by an allowed guardian, acting on the role of Asset Manager. | ||
|
||
## Functionality | ||
|
||
#### StrategicAssetsManager.sol | ||
|
||
`function withdrawERC20(address token, address to, uint256 amount) external onlyOwner` | ||
|
||
Sends ERC20 tokens to an address. Withdrawal mechanism. | ||
|
||
`function updateGuardian(address _manager) external onlyOwner` | ||
|
||
Updates guardian role, which in this contract functions as a strategic asset manager. Inherited from OwnableWithGuardian. | ||
|
||
`function transferOwnership(address _owner) external onlyOwner` | ||
|
||
Updates the owner of the contract. Inherited from Ownable. | ||
|
||
#### VeTokenManager.sol | ||
|
||
``` | ||
function buyBoost( | ||
address underlying, | ||
address delegator, | ||
address receiver, | ||
uint256 amount, | ||
uint256 duration | ||
) external onlyOwnerOrGuardian | ||
``` | ||
|
||
Purchase boost to incentivize rewards earned by locking (up to 2.5x of earnings). Spend fee token. | ||
For more info see: https://doc.paladin.vote/warden-boost/boost-market | ||
|
||
The idea is to increase the yield in the provided liquidity. | ||
For example, pay 10 BAL to boost rewards in a veBAL pool up to 2.5x times, to earn more BAL in return. | ||
|
||
``` | ||
function sellBoost( | ||
address underlying, | ||
uint256 pricePerVote, | ||
uint64 maxDuration, | ||
uint64 expiryTime, | ||
uint16 minPerc, | ||
uint16 maxPerc, | ||
bool useAdvicePrice | ||
) external onlyOwnerOrGuardian | ||
``` | ||
|
||
Owner of veToken allows others to incentivize their liquidity pools by selling boost. The price can be chosen by the user, or by setting useAdvicePrice, let Warden determine the price. | ||
The seller of boost receives the native token. | ||
|
||
``` | ||
function updateBoostOffer( | ||
address underlying, | ||
uint256 pricePerVote, | ||
uint64 maxDuration, | ||
uint64 expiryTime, | ||
uint16 minPerc, | ||
uint16 maxPerc, | ||
bool useAdvicePrice | ||
) external onlyOwnerOrGuardian | ||
``` | ||
|
||
Allows the user to update an existing offer to sell boost. | ||
|
||
`function removeBoostOffer(address underlying) external onlyOwnerOrGuardian` | ||
|
||
Removes a boost offer. | ||
|
||
` function claimBoostRewards(address underlying) external onlyOwnerOrGuardian` | ||
|
||
Claim rewards earned by selling boost. | ||
|
||
`function setSpaceIdVEBAL(address underlying, bytes32 _spaceId) external onlyOwnerOrGuardian` | ||
|
||
Sets the spaceID that's used by protocol on Snapshot for voting. For example, "balancer.eth" is Balancer's spaceId on Snapshot. | ||
|
||
``` | ||
function setDelegateVEBAL( | ||
address underlying, | ||
address newDelegate | ||
) external onlyOwnerOrGuardian | ||
``` | ||
|
||
Delegate tokens so they can vote on Snapshot. | ||
|
||
`function clearDelegateVEBAL(address underlying) external onlyOwnerOrGuardian` | ||
|
||
Remove the active delegate. | ||
|
||
``` | ||
function setLockDurationVEBAL( | ||
address underlying, | ||
uint256 newLockDuration | ||
) external onlyOwnerOrGuardian | ||
``` | ||
|
||
Set the lock duration to specific time. For example, max lock for veBAL is 1 year, so set to 1 year (or less). | ||
|
||
`function lockVEBAL(address underlying) external onlyOwnerOrGuardian` | ||
|
||
The main function for veBAL. | ||
Initially, it locks the B-80BAL-20WETH token to receive veBAL. (This contract needs to be allow-listed by Balancer prior to calling or it will fail). | ||
On subsequent calls (for example, weekly) it extends the lock duration once again. The voting % available per token is dependent on the locking duration. | ||
If locking duration is 6 months and the maximum duration is 1 year, then the voting weight is only half. | ||
This function also locks more of the native token held by StrategicAssetsManager available on the contract. | ||
|
||
`function unlockVEBAL(address underlying) external onlyOwnerOrGuardian` | ||
|
||
Unlocks the veToken in order to receive the underlying once again. Lock duration needs to have passed or transaction will revert. | ||
|
||
#### VlTokenManager.sol | ||
|
||
`function lockVLAURA(uint256 amount) external onlyOwnerOrGuardian` | ||
|
||
Locks AURA into vlAURA (if not locked before). | ||
|
||
`function claimVLAURARewards() external onlyOwnerOrGuardian` | ||
|
||
Claims rewards accrued by locking vlAURA. | ||
|
||
`function delegateVLAURA(address delegatee) external onlyOwnerOrGuardian` | ||
|
||
Delegates vlAURA for voting purposes. | ||
|
||
`function relockVLAURA() external onlyOwnerOrGuardian` | ||
|
||
Relocks vlAURA that has been previously locked. | ||
|
||
`function unlockVLAURA() external onlyOwnerOrGuardian` | ||
|
||
Unlock vlAURA position into AURA. Lock period needs to have passed or it will revert. | ||
|
||
`function emergencyWithdrawVLAURA() external onlyOwnerOrGuardian` | ||
|
||
Emergency function to exit a position if the AURA system is shut down. | ||
|
||
##### LSDLiquidityGaugeManager.sol | ||
|
||
`function setGaugeController(address token, address gaugeController) public onlyOwnerOrGuardian` | ||
|
||
Sets the address that handles gauges for veTokens. | ||
|
||
Here is the proposal on Balancer as it relates to GHO: https://forum.balancer.fi/t/bip-xxx-approve-the-smbpt-gauges-for-the-aave-sm/4949 | ||
This post has the explanation on all the steps the DAO can expect to interact with these protocols to maximize rewards. | ||
The excalidraw towards the bottom of the page is helpful in seeing the full flow. | ||
|
||
Curve docs on liquidity gauges: https://curve.readthedocs.io/dao-gauges.html | ||
|
||
The main concept here is that the ecosystem rewards liquidity providers by rewarding them with token emissions. These tokens are distributed according to which gauges receive the | ||
most votes. | ||
|
||
``` | ||
function voteForGaugeWeight( | ||
address token, | ||
address gauge, | ||
uint256 weight | ||
) external onlyOwnerOrGuardian | ||
``` | ||
|
||
Utilizing the veToken holdings, the DAO can vote to redirect emissions to the DAO's own gauge. | ||
Here, by voting for the DAO's gauge, and also purchasing boost, the DAO can expect to earn a lot more BAL rewards over time than just by holding a veToken for example. |
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: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; | ||
import {Initializable} from 'solidity-utils/contracts/transparent-proxy/Initializable.sol'; | ||
import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; | ||
import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol'; | ||
import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; | ||
|
||
import {LSDLiquidityGaugeManager} from './LSDLiquidityGaugeManager.sol'; | ||
import {VeTokenManager} from './VeTokenManager.sol'; | ||
import {VlTokenManager} from './VlTokenManager.sol'; | ||
|
||
/// @author Llama | ||
contract StrategicAssetsManager is | ||
Initializable, | ||
LSDLiquidityGaugeManager, | ||
VeTokenManager, | ||
VlTokenManager | ||
{ | ||
using SafeERC20 for IERC20; | ||
|
||
event WithdrawalERC20(address indexed _token, uint256 _amount); | ||
|
||
/// @notice Initialize function | ||
function initialize() external initializer { | ||
_transferOwnership(AaveGovernanceV2.SHORT_EXECUTOR); | ||
_updateGuardian(0x205e795336610f5131Be52F09218AF19f0f3eC60); | ||
spaceIdBalancer = 'balancer.eth'; | ||
gaugeControllerBalancer = 0xC128468b7Ce63eA702C1f104D55A2566b13D3ABD; | ||
lockDurationVEBAL = 365 days; | ||
} | ||
|
||
/// @notice Withdraw a specified amount of ERC20 token to the Aave Collector | ||
/// @param token The address of the ERC20 token to withdraw | ||
/// @param amount The amount of token to withdraw | ||
function withdrawERC20(address token, uint256 amount) external onlyOwner { | ||
IERC20(token).safeTransfer(address(AaveV3Ethereum.COLLECTOR), amount); | ||
emit WithdrawalERC20(token, amount); | ||
} | ||
} |
Oops, something went wrong.
fb9c5de
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Foundry report
Build log
Test success 🌈