diff --git a/contracts/contracts/harvest/OETHHarvesterSimple.sol b/contracts/contracts/harvest/OETHHarvesterSimple.sol index 6acc3f0944..3b0729e590 100644 --- a/contracts/contracts/harvest/OETHHarvesterSimple.sol +++ b/contracts/contracts/harvest/OETHHarvesterSimple.sol @@ -48,6 +48,9 @@ contract OETHHarvesterSimple is Initializable, Strategizable { //////////////////////////////////////////////////// constructor(address _wrappedNativeToken) { wrappedNativeToken = _wrappedNativeToken; + + // prevent implementation contract to be governed + _setGovernor(address(1)); } /// @notice Initialize the contract diff --git a/contracts/contracts/harvest/OSonicHarvester.sol b/contracts/contracts/harvest/OSonicHarvester.sol new file mode 100644 index 0000000000..f1cf642a34 --- /dev/null +++ b/contracts/contracts/harvest/OSonicHarvester.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { SuperOETHHarvester } from "./SuperOETHHarvester.sol"; + +contract OSonicHarvester is SuperOETHHarvester { + /// @param _wrappedNativeToken Address of the native Wrapped S (wS) token + constructor(address _wrappedNativeToken) + SuperOETHHarvester(_wrappedNativeToken) + {} +} diff --git a/contracts/contracts/harvest/README.md b/contracts/contracts/harvest/README.md index dbc3be1d35..82389cebb2 100644 --- a/contracts/contracts/harvest/README.md +++ b/contracts/contracts/harvest/README.md @@ -2,6 +2,8 @@ ## Dripper +Used by OUSD on Mainnet. + ### Hierarchy ![Dripper Hierarchy](../../docs/DripperHierarchy.svg) @@ -14,22 +16,26 @@ ![Dripper Storage](../../docs/DripperStorage.svg) -## OETH Dripper +## Fixed Rate Dripper + +Used on Mainnet for OETH, Base and Sonic. ### Hierarchy -![OETH Dripper Hierarchy](../../docs/OETHDripperHierarchy.svg) +![Fixed Rate Dripper Hierarchy](../../docs/OETHFixedRateDripperHierarchy.svg) ### Squashed -![OETH Dripper Squashed](../../docs/OETHDripperSquashed.svg) +![Fixed Rate Dripper Squashed](../../docs/OETHFixedRateDripperSquashed.svg) ### Storage -![OETH Dripper Storage](../../docs/OETHDripperStorage.svg) +![Fixed Rate Dripper Storage](../../docs/OETHFixedRateDripperStorage.svg) ## Harvester +Used on Mainnet for OUSD. + ### Hierarchy ![Harvester Hierarchy](../../docs/HarvesterHierarchy.svg) @@ -42,16 +48,46 @@ ![Harvester Storage](../../docs/HarvesterStorage.svg) -## OETH Harvester +## OETH Simple Harvester + +Used on Mainnet for OETH. + +### Hierarchy + +![OETH Simple Harvester Hierarchy](../../docs/OETHHarvesterSimpleHierarchy.svg) + +### Squashed + +![OETH Simple Harvester Squashed](../../docs/OETHHarvesterSimpleSquashed.svg) + +### Storage + +![OETH Simple Harvester Storage](../../docs/OETHHarvesterSimpleStorage.svg) + +## Base Harvester + +### Hierarchy + +![OETH Base Harvester Hierarchy](../../docs/OETHBaseHarvesterHierarchy.svg) + +### Squashed + +![OETH Base Harvester Squashed](../../docs/OETHBaseHarvesterSquashed.svg) + +### Storage + +![OETH Base Harvester Storage](../../docs/OETHBaseHarvesterStorage.svg) + +## Sonic Harvester ### Hierarchy -![OETH Harvester Hierarchy](../../docs/OETHHarvesterHierarchy.svg) +![Sonic Harvester Hierarchy](../../docs/OSonicHarvesterHierarchy.svg) ### Squashed -![OETH Harvester Squashed](../../docs/OETHHarvesterSquashed.svg) +![Sonic Harvester Squashed](../../docs/OSonicHarvesterSquashed.svg) ### Storage -![OETH Harvester Storage](../../docs/OETHHarvesterStorage.svg) +![Sonic Harvester Storage](../../docs/OSonicHarvesterStorage.svg) diff --git a/contracts/contracts/interfaces/sonic/ISwapXGauge.sol b/contracts/contracts/interfaces/sonic/ISwapXGauge.sol new file mode 100644 index 0000000000..9e5def5e5a --- /dev/null +++ b/contracts/contracts/interfaces/sonic/ISwapXGauge.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IGauge { + function TOKEN() external view returns (address); + + function balanceOf(address account) external view returns (uint256); + + function claimFees() external returns (uint256 claimed0, uint256 claimed1); + + function deposit(uint256 amount) external; + + function depositAll() external; + + function earned(address account) external view returns (uint256); + + function getReward() external; + + function getReward(address _user) external; + + function isForPair() external view returns (bool); + + function lastTimeRewardApplicable() external view returns (uint256); + + function lastUpdateTime() external view returns (uint256); + + function notifyRewardAmount(address token, uint256 reward) external; + + function periodFinish() external view returns (uint256); + + function rewardForDuration() external view returns (uint256); + + function rewardPerToken() external view returns (uint256); + + function rewardPerTokenStored() external view returns (uint256); + + function rewardRate() external view returns (uint256); + + function rewardToken() external view returns (address); + + function rewards(address) external view returns (uint256); + + function totalSupply() external view returns (uint256); + + function userRewardPerTokenPaid(address) external view returns (uint256); + + function withdraw(uint256 amount) external; + + function withdrawAll() external; + + function withdrawAllAndHarvest() external; + + function withdrawExcess(address token, uint256 amount) external; +} diff --git a/contracts/contracts/interfaces/sonic/ISwapXPair.sol b/contracts/contracts/interfaces/sonic/ISwapXPair.sol new file mode 100644 index 0000000000..347957bc33 --- /dev/null +++ b/contracts/contracts/interfaces/sonic/ISwapXPair.sol @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IPair { + event Approval(address indexed src, address indexed guy, uint256 wad); + event Transfer(address indexed src, address indexed dst, uint256 wad); + + event Mint(address indexed sender, uint256 amount0, uint256 amount1); + event Burn( + address indexed sender, + uint256 amount0, + uint256 amount1, + address indexed to + ); + event Swap( + address indexed sender, + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out, + address indexed to + ); + event Claim( + address indexed sender, + address indexed recipient, + uint256 amount0, + uint256 amount1 + ); + + function metadata() + external + view + returns ( + uint256 dec0, + uint256 dec1, + uint256 r0, + uint256 r1, + bool st, + address t0, + address t1 + ); + + function claimFees() external returns (uint256, uint256); + + function tokens() external view returns (address, address); + + function token0() external view returns (address); + + function token1() external view returns (address); + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function swap( + uint256 amount0Out, + uint256 amount1Out, + address to, + bytes calldata data + ) external; + + function burn(address to) + external + returns (uint256 amount0, uint256 amount1); + + function mint(address to) external returns (uint256 liquidity); + + function getReserves() + external + view + returns ( + uint256 _reserve0, + uint256 _reserve1, + uint256 _blockTimestampLast + ); + + function getAmountOut(uint256, address) external view returns (uint256); + + // ERC20 methods + function name() external view returns (string memory); + + function symbol() external view returns (string memory); + + function decimals() external view returns (uint8); + + function totalSupply() external view returns (uint256); + + function balanceOf(address) external view returns (uint256); + + function transfer(address recipient, uint256 amount) + external + returns (bool); + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); + + function allowance(address owner, address spender) + external + view + returns (uint256); + + function approve(address spender, uint256 value) external returns (bool); + + function claimable0(address _user) external view returns (uint256); + + function claimable1(address _user) external view returns (uint256); + + function isStable() external view returns (bool); + + function skim(address to) external; + + function sync() external; +} diff --git a/contracts/contracts/interfaces/sonic/IVoterV3.sol b/contracts/contracts/interfaces/sonic/IVoterV3.sol new file mode 100644 index 0000000000..451a274e7b --- /dev/null +++ b/contracts/contracts/interfaces/sonic/IVoterV3.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IVoterV3 { + /// @notice create a gauge + function createGauge(address _pool, uint256 _gaugeType) + external + returns ( + address _gauge, + address _internal_bribe, + address _external_bribe + ); + + function gauges(address _pool) external view returns (address _gauge); +} diff --git a/contracts/contracts/interfaces/sonic/IWrappedSonic.sol b/contracts/contracts/interfaces/sonic/IWrappedSonic.sol index 7991187436..fe05cdef6d 100644 --- a/contracts/contracts/interfaces/sonic/IWrappedSonic.sol +++ b/contracts/contracts/interfaces/sonic/IWrappedSonic.sol @@ -2,6 +2,15 @@ pragma solidity ^0.8.0; interface IWrappedSonic { + event Deposit(address indexed account, uint256 value); + event Withdrawal(address indexed account, uint256 value); + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); + function allowance(address owner, address spender) external view diff --git a/contracts/contracts/proxies/SonicProxies.sol b/contracts/contracts/proxies/SonicProxies.sol index 1f5e7c41f4..d59d3a0477 100644 --- a/contracts/contracts/proxies/SonicProxies.sol +++ b/contracts/contracts/proxies/SonicProxies.sol @@ -39,8 +39,22 @@ contract SonicStakingStrategyProxy is InitializeGovernedUpgradeabilityProxy { } /** - * @notice OSonicHarvesterProxy delegates calls to a OETHHarvesterSimple implementation + * @notice OSonicHarvesterProxy delegates calls to a OSonicHarvester implementation */ contract OSonicHarvesterProxy is InitializeGovernedUpgradeabilityProxy { } + +/** + * @notice SonicCurveAMOStrategyProxy delegates calls to a SonicCurveAMOStrategy implementation + */ +contract SonicCurveAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy { + +} + +/** + * @notice SonicSwapXAMOStrategyProxy delegates calls to a SonicSwapXAMOStrategy implementation + */ +contract SonicSwapXAMOStrategyProxy is InitializeGovernedUpgradeabilityProxy { + +} diff --git a/contracts/contracts/strategies/BaseCurveAMOStrategy.sol b/contracts/contracts/strategies/BaseCurveAMOStrategy.sol index dd6910a161..0b473c93db 100644 --- a/contracts/contracts/strategies/BaseCurveAMOStrategy.sol +++ b/contracts/contracts/strategies/BaseCurveAMOStrategy.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.0; /** @@ -60,8 +60,8 @@ contract BaseCurveAMOStrategy is InitializableAbstractStrategy { IChildLiquidityGaugeFactory public immutable gaugeFactory; // Ordered list of pool assets - uint128 public constant oethCoinIndex = 1; - uint128 public constant wethCoinIndex = 0; + uint128 public immutable oethCoinIndex; + uint128 public immutable wethCoinIndex; /** * @notice Maximum slippage allowed for adding/removing liquidity from the Curve pool. @@ -124,8 +124,13 @@ contract BaseCurveAMOStrategy is InitializableAbstractStrategy { address _oeth, address _weth, address _gauge, - address _gaugeFactory + address _gaugeFactory, + uint128 _oethCoinIndex, + uint128 _wethCoinIndex ) InitializableAbstractStrategy(_baseConfig) { + oethCoinIndex = _oethCoinIndex; + wethCoinIndex = _wethCoinIndex; + lpToken = IERC20(_baseConfig.platformAddress); curvePool = ICurveStableSwapNG(_baseConfig.platformAddress); @@ -335,6 +340,8 @@ contract BaseCurveAMOStrategy is InitializableAbstractStrategy { */ function withdrawAll() external override onlyVaultOrGovernor nonReentrant { uint256 gaugeTokens = gauge.balanceOf(address(this)); + // Can not withdraw zero LP tokens from the gauge + if (gaugeTokens == 0) return; _lpWithdraw(gaugeTokens); // Withdraws are proportional to assets held by 3Pool diff --git a/contracts/contracts/strategies/sonic/README.md b/contracts/contracts/strategies/sonic/README.md index 87e25ccdc2..3cd7f3b743 100644 --- a/contracts/contracts/strategies/sonic/README.md +++ b/contracts/contracts/strategies/sonic/README.md @@ -13,3 +13,31 @@ ### Storage ![Sonic Staking Strategy Storage](../../../docs/SonicStakingStrategyStorage.svg) + +## Curve AMO Strategy + +### Hierarchy + +![Curve AMO Strategy Hierarchy](../../../docs/SonicCurveAMOStrategyHierarchy.svg) + +### Squashed + +![Curve AMO Strategy Squashed](../../../docs/SonicCurveAMOStrategySquashed.svg) + +### Storage + +![Curve AMO Strategy Storage](../../../docs/SonicCurveAMOStrategyStorage.svg) + +## SwapX AMO Strategy + +### Hierarchy + +![SwapX AMO Strategy Hierarchy](../../../docs/SonicSwapXAMOStrategyHierarchy.svg) + +### Squashed + +![SwapX AMO Strategy Squashed](../../../docs/SonicSwapXAMOStrategySquashed.svg) + +### Storage + +![SwapX AMO Strategy Storage](../../../docs/SonicSwapXAMOStrategyStorage.svg) diff --git a/contracts/contracts/strategies/sonic/SonicCurveAMOStrategy.sol b/contracts/contracts/strategies/sonic/SonicCurveAMOStrategy.sol new file mode 100644 index 0000000000..972a4714a5 --- /dev/null +++ b/contracts/contracts/strategies/sonic/SonicCurveAMOStrategy.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { BaseCurveAMOStrategy } from "../BaseCurveAMOStrategy.sol"; + +/** + * @title Curve AMO Strategy for the Origin Sonic Vault + * @author Origin Protocol Inc + */ +contract SonicCurveAMOStrategy is BaseCurveAMOStrategy { + constructor( + BaseStrategyConfig memory _baseConfig, + address _os, + address _ws, + address _gauge, + address _gaugeFactory, + uint128 _osCoinIndex, + uint128 _wsCoinIndex + ) + BaseCurveAMOStrategy( + _baseConfig, + _os, + _ws, + _gauge, + _gaugeFactory, + _osCoinIndex, + _wsCoinIndex + ) + {} +} diff --git a/contracts/contracts/strategies/sonic/SonicSwapXAMOStrategy.sol b/contracts/contracts/strategies/sonic/SonicSwapXAMOStrategy.sol new file mode 100644 index 0000000000..f403bbeaf3 --- /dev/null +++ b/contracts/contracts/strategies/sonic/SonicSwapXAMOStrategy.sol @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +/** + * @title SwapX Automated Market Maker (AMO) Strategy + * @notice AMO strategy for the SwapX OS/wS stable pool + * @author Origin Protocol Inc + */ +import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import { IERC20, InitializableAbstractStrategy } from "../../utils/InitializableAbstractStrategy.sol"; +import { StableMath } from "../../utils/StableMath.sol"; +import { sqrt } from "../../utils/PRBMath.sol"; +import { IBasicToken } from "../../interfaces/IBasicToken.sol"; +import { IPair } from "../../interfaces/sonic/ISwapXPair.sol"; +import { IGauge } from "../../interfaces/sonic/ISwapXGauge.sol"; +import { IVault } from "../../interfaces/IVault.sol"; + +contract SonicSwapXAMOStrategy is InitializableAbstractStrategy { + using SafeERC20 for IERC20; + using StableMath for uint256; + using SafeCast for uint256; + + /** + * @notice a threshold under which the contract no longer allows for the protocol to manually rebalance. + * Guarding against a strategist / guardian being taken over and with multiple transactions + * draining the protocol funds. + */ + uint256 public constant SOLVENCY_THRESHOLD = 0.998 ether; + + /// @notice Precision for the SwapX Stable AMM (sAMM) invariant k. + uint256 public constant PRECISION = 1e18; + + /// @notice Address of the Wrapped S (wS) token. + address public immutable ws; + + /// @notice Address of the OS token contract. + address public immutable os; + + /// @notice Address of the SwapX Stable pool contract. + address public immutable pool; + + /// @notice Address of the SwapX Gauge contract. + address public immutable gauge; + + event SwapOTokensToPool( + uint256 osMinted, + uint256 wsLiquidity, + uint256 osLiquidity, + uint256 lpTokens + ); + event SwapAssetsToPool( + uint256 wsSwapped, + uint256 lpTokens, + uint256 osBurnt + ); + + /** + * @dev Verifies that the caller is the Strategist of the Vault. + */ + modifier onlyStrategist() { + require( + msg.sender == IVault(vaultAddress).strategistAddr(), + "Caller is not the Strategist" + ); + _; + } + + /** + * @dev Skim the SwapX pool in case any extra wS or OS tokens were added + */ + modifier skimPool() { + IPair(pool).skim(address(this)); + _; + } + + /** + * @dev Checks the pool's balances have improved and the balances + * have not tipped to the other side. + * This modifier is only applied to functions that do swaps against the pool. + * Deposits and withdrawals are proportional to the pool's balances hence don't need this check. + */ + modifier improvePoolBalance() { + // Get the asset and OToken balances in the pool + (uint256 wsReservesBefore, uint256 osReservesBefore, ) = IPair(pool) + .getReserves(); + // diff = wS balance - OS balance + int256 diffBefore = wsReservesBefore.toInt256() - + osReservesBefore.toInt256(); + + _; + + // Get the asset and OToken balances in the pool + (uint256 wsReservesAfter, uint256 osReservesAfter, ) = IPair(pool) + .getReserves(); + // diff = wS balance - OS balance + int256 diffAfter = wsReservesAfter.toInt256() - + osReservesAfter.toInt256(); + + if (diffBefore == 0) { + require(diffAfter == 0, "Position balance is worsened"); + } else if (diffBefore < 0) { + // If the pool was originally imbalanced in favor of OS, then + // we want to check that the pool is now more balanced + require(diffAfter <= 0, "Assets overshot peg"); + require(diffBefore < diffAfter, "OTokens balance worse"); + } else if (diffBefore > 0) { + // If the pool was originally imbalanced in favor of wS, then + // we want to check that the pool is now more balanced + require(diffAfter >= 0, "OTokens overshot peg"); + require(diffAfter < diffBefore, "Assets balance worse"); + } + } + + /** + * @param _baseConfig The `platformAddress` is the address of the SwapX pool. + * The `vaultAddress` is the address of the Origin Sonic Vault. + * @param _os Address of the OS token. + * @param _ws Address of the Wrapped S (wS) token. + * @param _gauge Address of the SwapX gauge for the pool. + */ + constructor( + BaseStrategyConfig memory _baseConfig, + address _os, + address _ws, + address _gauge + ) InitializableAbstractStrategy(_baseConfig) { + // Check the pool tokens are correct + require( + IPair(_baseConfig.platformAddress).token0() == _ws && + IPair(_baseConfig.platformAddress).token1() == _os, + "Incorrect pool tokens" + ); + // Checked both tokens are to 18 decimals + require( + IBasicToken(_ws).decimals() == 18 && + IBasicToken(_os).decimals() == 18, + "Incorrect token decimals" + ); + // Check the SwapX pool is a Stable AMM (sAMM) + require( + IPair(_baseConfig.platformAddress).isStable() == true, + "Pool not stable" + ); + // Check the gauge is for the pool + require( + IGauge(_gauge).TOKEN() == _baseConfig.platformAddress, + "Incorrect gauge" + ); + + // Set the immutable variables + os = _os; + ws = _ws; + pool = _baseConfig.platformAddress; + gauge = _gauge; + + // This is an implementation contract. The governor is set in the proxy contract. + _setGovernor(address(0)); + } + + /** + * Initializer for setting up strategy internal state. This overrides the + * InitializableAbstractStrategy initializer as SwapX strategies don't fit + * well within that abstraction. + * @param _rewardTokenAddresses Address of SWPx token + */ + function initialize(address[] calldata _rewardTokenAddresses) + external + onlyGovernor + initializer + { + address[] memory pTokens = new address[](1); + pTokens[0] = pool; + + address[] memory _assets = new address[](1); + _assets[0] = ws; + + InitializableAbstractStrategy._initialize( + _rewardTokenAddresses, + _assets, + pTokens + ); + + _approveBase(); + } + + /*************************************** + Deposit + ****************************************/ + + /** + * @notice Deposit Wrapped S (wS) into the SwapX pool + * @param _asset Address of Wrapped S (wS) contract. + * @param _amount Amount of Wrapped S (wS) to deposit. + */ + function deposit(address _asset, uint256 _amount) + external + override + onlyVault + nonReentrant + skimPool + { + _deposit(_asset, _amount); + } + + function _deposit(address _wS, uint256 _wsAmount) internal { + require(_wsAmount > 0, "Must deposit something"); + require(_wS == ws, "Unsupported asset"); + + // Calculate the required amount of OS to mint based on the wS amount. + uint256 osLiquidity = _calcTokensToMint(_wsAmount); + + // Mint the required OS tokens to this strategy + IVault(vaultAddress).mintForStrategy(osLiquidity); + + // Add wS and OS liquidity to the pool and stake in gauge + _depositToPoolAndGauge(_wsAmount, osLiquidity); + + // Ensure solvency of the vault + _solvencyAssert(); + + // Emit event for the deposited wS tokens + emit Deposit(_wS, pool, _wsAmount); + // Emit event for the minted OS tokens + emit Deposit(os, pool, osLiquidity); + } + + /** + * @notice Deposit the strategy's entire balance of Wrapped S (wS) into the pool + */ + function depositAll() external override onlyVault nonReentrant skimPool { + uint256 balance = IERC20(ws).balanceOf(address(this)); + if (balance > 0) { + _deposit(ws, balance); + } + } + + /*************************************** + Withdraw + ****************************************/ + + /** + * @notice Withdraw wS and OS from the SwapX pool, burn the OS, + * and transfer the wS to the recipient. + * @param _recipient Address to receive withdrawn asset which is normally the Vault. + * @param _asset Address of the Wrapped S (wS) contract. + * @param _wsAmount Amount of Wrapped S (wS) to withdraw. + */ + function withdraw( + address _recipient, + address _asset, + uint256 _wsAmount + ) external override onlyVault nonReentrant skimPool { + require(_wsAmount > 0, "Must withdraw something"); + require(_asset == ws, "Unsupported asset"); + + // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back + uint256 lpTokens = _calcTokensToBurn(_wsAmount); + + // Withdraw pool LP tokens from the gauge and remove assets from from the pool + _withdrawFromGaugeAndPool(lpTokens); + + // Burn all the removed OS and any that was left in the strategy + uint256 osToBurn = IERC20(os).balanceOf(address(this)); + IVault(vaultAddress).burnForStrategy(osToBurn); + + // Transfer wS to the recipient + // Note there can be a dust amount of wS left in the strategy as + // the burn of the pool's LP tokens is rounded up + require( + IERC20(ws).balanceOf(address(this)) >= _wsAmount, + "Not enough wS removed from pool" + ); + IERC20(ws).safeTransfer(_recipient, _wsAmount); + + // Ensure solvency of the vault + _solvencyAssert(); + + // Emit event for the withdrawn wS tokens + emit Withdrawal(_asset, pool, _wsAmount); + // Emit event for the burnt OS tokens + emit Withdrawal(os, pool, osToBurn); + } + + /** + * @notice Withdraw all pool LP tokens from the gauge, + * remove all wS and OS from the SwapX pool, + * burn all the OS tokens, + * and transfer all the wS to the Vault contract. + * @dev There is no solvency check here as withdrawAll can be called to + * quickly secure assets to the Vault in emergencies. + */ + function withdrawAll() + external + override + onlyVaultOrGovernor + nonReentrant + skimPool + { + // Get all the pool LP tokens the strategy has staked in the gauge + uint256 lpTokens = IGauge(gauge).balanceOf(address(this)); + // Can not withdraw zero LP tokens from the gauge + if (lpTokens == 0) return; + + // Withdraw pool LP tokens from the gauge and remove assets from from the pool + _withdrawFromGaugeAndPool(lpTokens); + + // Burn all OS in this strategy contract + uint256 osToBurn = IERC20(os).balanceOf(address(this)); + IVault(vaultAddress).burnForStrategy(osToBurn); + + // Get the strategy contract's wS balance. + // This includes all that was removed from the SwapX pool and + // any that was sitting in the strategy contract before the removal. + uint256 wsBalance = IERC20(ws).balanceOf(address(this)); + IERC20(ws).safeTransfer(vaultAddress, wsBalance); + + // Emit event for the withdrawn wS tokens + emit Withdrawal(ws, pool, wsBalance); + // Emit event for the burnt OS tokens + emit Withdrawal(os, pool, osToBurn); + } + + /*************************************** + Pool Rebalancing + ****************************************/ + + /** @notice Used when there is more OS than wS in the pool. + * wS and OS is removed from the pool, the received wS is swapped for OS + * and the left over OS in the strategy is burnt. + * The OS/wS price is < 1.0 so OS is being bought at a discount. + * @param _wsAmount Amount of Wrapped S (wS) to swap into the pool. + */ + function swapAssetsToPool(uint256 _wsAmount) + external + onlyStrategist + nonReentrant + improvePoolBalance + skimPool + { + require(_wsAmount > 0, "Must swap something"); + + // 1. Partially remove liquidity so there’s enough wS for the swap + + // Calculate how much pool LP tokens to burn to get the required amount of wS tokens back + uint256 lpTokens = _calcTokensToBurn(_wsAmount); + require(lpTokens > 0, "No LP tokens to burn"); + + _withdrawFromGaugeAndPool(lpTokens); + + // 2. Swap wS for OS against the pool + // Swap exact amount of wS for OS against the pool + // There can be a dust amount of wS left in the strategy as the burn of the pool's LP tokens is rounded up + _swapExactTokensForTokens(_wsAmount, ws, os); + + // 3. Burn all the OS left in the strategy from the remove liquidity and swap + uint256 osToBurn = IERC20(os).balanceOf(address(this)); + IVault(vaultAddress).burnForStrategy(osToBurn); + + // Ensure solvency of the vault + _solvencyAssert(); + + emit SwapAssetsToPool(_wsAmount, lpTokens, osToBurn); + } + + /** + * @notice Used when there is more wS than OS in the pool. + * OS is minted and swapped for wS against the pool, + * more OS is minted and added back into the pool with the swapped out wS. + * The OS/wS price is > 1.0 so OS is being sold at a premium. + * @param _osAmount Amount of OS to swap into the pool. + */ + function swapOTokensToPool(uint256 _osAmount) + external + onlyStrategist + nonReentrant + improvePoolBalance + skimPool + { + require(_osAmount > 0, "Must swap something"); + + // 1. Mint OS so it can be swapped into the pool + + // There shouldn't be any OS in the strategy but just in case + uint256 osInStrategy = IERC20(os).balanceOf(address(this)); + require(_osAmount >= osInStrategy, "Too much OS in strategy"); + uint256 osToMint = _osAmount - osInStrategy; + + // Mint the required OS tokens to this strategy + IVault(vaultAddress).mintForStrategy(osToMint); + + // 2. Swap OS for wS against the pool + _swapExactTokensForTokens(_osAmount, os, ws); + + // 3. Add wS and OS back to the pool in proportion to the pool's reserves + + // The wS is from the swap and any wS that was sitting in the strategy + uint256 wsLiquidity = IERC20(ws).balanceOf(address(this)); + // Calculate the required amount of OS to mint based on the wS amount. + uint256 osLiquidity = _calcTokensToMint(wsLiquidity); + + // Mint more OS to this strategy so they can then be added to the pool + IVault(vaultAddress).mintForStrategy(osLiquidity); + + // Add wS and OS liquidity to the pool and stake in gauge + uint256 lpTokens = _depositToPoolAndGauge(wsLiquidity, osLiquidity); + + // Ensure solvency of the vault + _solvencyAssert(); + + emit SwapOTokensToPool(osToMint, wsLiquidity, osLiquidity, lpTokens); + } + + /*************************************** + Assets and Rewards + ****************************************/ + + /** + * @notice Get the wS value of assets in the strategy and SwapX pool. + * The value of the assets in the pool is calculated assuming the pool is balanced. + * This way the value can not be manipulated by changing the pool's token balances. + * @param _asset Address of the Wrapped S (wS) token + * @return balance Total value in wS. + */ + function checkBalance(address _asset) + public + view + override + returns (uint256 balance) + { + require(_asset == ws, "Unsupported asset"); + + // wS balance needed here for the balance check that happens from vault during depositing. + balance = IERC20(ws).balanceOf(address(this)); + + // This assumes 1 gauge LP token = 1 pool LP token + uint256 lpTokens = IGauge(gauge).balanceOf(address(this)); + if (lpTokens == 0) return balance; + + // Add the strategy’s share of the wS and OS tokens in the SwapX pool if the pool was balanced. + balance += _lpValue(lpTokens); + } + + /** + * @notice Returns bool indicating whether asset is supported by strategy + * @param _asset Address of the asset + */ + function supportsAsset(address _asset) public view override returns (bool) { + return _asset == ws; + } + + /** + * @notice Collect accumulated SWPx (and other) rewards and send to the Harvester. + */ + function collectRewardTokens() + external + override + onlyHarvester + nonReentrant + { + // Collect SWPx rewards from the gauge + IGauge(gauge).getReward(); + + _collectRewardTokens(); + } + + /*************************************** + Internal SwapX Pool and Gauge Functions + ****************************************/ + + /** + * @dev Calculate the required amount of OS to mint based on the wS amount. + * This ensures the proportion of OS tokens being added to the pool matches the proportion of wS tokens. + * For example, if the added wS tokens is 10% of existing wS tokens in the pool, + * then the OS tokens being added should also be 10% of the OS tokens in the pool. + * @param _wsAmount Amount of Wrapped S (wS) to be added to the pool. + * @return osAmount Amount of OS to be minted and added to the pool. + */ + function _calcTokensToMint(uint256 _wsAmount) + internal + view + returns (uint256 osAmount) + { + (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves(); + require(wsReserves > 0, "Empty pool"); + + // OS to add = (wS being added * OS in pool) / wS in pool + osAmount = (_wsAmount * osReserves) / wsReserves; + } + + /** + * @dev Calculate how much pool LP tokens to burn to get the required amount of wS tokens back + * from the pool. + * @param _wsAmount Amount of Wrapped S (wS) to be removed from the pool. + * @return lpTokens Amount of SwapX pool LP tokens to burn. + */ + function _calcTokensToBurn(uint256 _wsAmount) + internal + view + returns (uint256 lpTokens) + { + /* The rate between coins in the pool determines the rate at which pool returns + * tokens when doing balanced removal (remove_liquidity call). And by knowing how much wS + * we want we can determine how much of OS we receive by removing liquidity. + * + * Because we are doing balanced removal we should be making profit when removing liquidity in a + * pool tilted to either side. + * + * Important: A downside is that the Strategist / Governor needs to be + * cognizant of not removing too much liquidity. And while the proposal to remove liquidity + * is being voted on, the pool tilt might change so much that the proposal that has been valid while + * created is no longer valid. + */ + + (uint256 wsReserves, , ) = IPair(pool).getReserves(); + require(wsReserves > 0, "Empty pool"); + + lpTokens = (_wsAmount * IPair(pool).totalSupply()) / wsReserves; + lpTokens += 1; // Add 1 to ensure we get enough LP tokens with rounding + } + + /** + * @dev Deposit Wrapped S (wS) and OS liquidity to the SwapX pool + * and stake the pool's LP token in the gauge. + * @param _wsAmount Amount of Wrapped S (wS) to deposit. + * @param osAmount Amount of OS to deposit. + * @return lpTokens Amount of SwapX pool LP tokens minted. + */ + function _depositToPoolAndGauge(uint256 _wsAmount, uint256 osAmount) + internal + returns (uint256 lpTokens) + { + // Transfer wS to the pool + IERC20(ws).safeTransfer(pool, _wsAmount); + // Transfer OS to the pool + IERC20(os).safeTransfer(pool, osAmount); + + // Mint LP tokens from the pool + lpTokens = IPair(pool).mint(address(this)); + + // Deposit the pool's LP tokens into the gauge + IGauge(gauge).deposit(lpTokens); + } + + /** + * @dev Withdraw pool LP tokens from the gauge and remove wS and OS from the pool. + * @param lpTokens Amount of SwapX pool LP tokens to withdraw from the gauge + */ + function _withdrawFromGaugeAndPool(uint256 lpTokens) internal { + require( + IGauge(gauge).balanceOf(address(this)) >= lpTokens, + "Not enough LP tokens in gauge" + ); + + // Withdraw pool LP tokens from the gauge + IGauge(gauge).withdraw(lpTokens); + + // Transfer the pool LP tokens to the pool + IERC20(pool).safeTransfer(pool, lpTokens); + + // Burn the LP tokens and transfer the wS and OS back to the strategy + IPair(pool).burn(address(this)); + } + + /** + * @dev Swap exact amount of tokens for another token against the pool. + * @param _amountIn Amount of tokens to swap into the pool. + * @param _tokenIn Address of the token going into the pool. + * @param _tokenOut Address of the token being swapped out of the pool. + */ + function _swapExactTokensForTokens( + uint256 _amountIn, + address _tokenIn, + address _tokenOut + ) internal { + // Transfer in tokens to the pool + IERC20(_tokenIn).safeTransfer(pool, _amountIn); + + // Calculate how much out tokens we get from the swap + uint256 amountOut = IPair(pool).getAmountOut(_amountIn, _tokenIn); + + // Safety check that we are dealing with the correct pool tokens + require( + (_tokenIn == ws || _tokenIn == os) && + (_tokenOut == ws || _tokenOut == os), + "Unsupported swap" + ); + + // Work out the correct order of the amounts for the pool + (uint256 amount0, uint256 amount1) = _tokenIn == ws + ? (uint256(0), amountOut) + : (amountOut, 0); + + // Perform the swap on the pool + IPair(pool).swap(amount0, amount1, address(this), new bytes(0)); + + // The slippage protection against the amount out is indirectly done + // via the improvePoolBalance + } + + /// @dev Calculate the value of a LP position in a SwapX stable pool + /// if the pool was balanced. + /// @param lpTokens Amount of LP tokens in the SwapX pool + /// @return value The wS value of the LP tokens in the pool + function _lpValue(uint256 lpTokens) internal view returns (uint256 value) { + // Get total supply of LP tokens + uint256 totalSupply = IPair(pool).totalSupply(); + require(totalSupply > 0, "No liquidity in pool"); + + // Get the current reserves of the pool + (uint256 wsReserves, uint256 osReserves, ) = IPair(pool).getReserves(); + + // Calculate the invariant of the pool assuming both tokens have 18 decimals. + // k is scaled to 18 decimals. + uint256 k = _invariant(wsReserves, osReserves); + + // If x = y, let’s denote x = y = z (where z is the common reserve value) + // Substitute z into the invariant: + // k = z^3 * z + z * z^3 + // k = 2 * z^4 + // Going back the other way to calculate the common reserve value z + // z = (k / 2) ^ (1/4) + // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4) + uint256 zSquared = sqrt((k * 1e18) / 2); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt + uint256 z = sqrt(zSquared * 1e18); // 18 + 18 = 36 decimals becomes 18 decimals after sqrt + uint256 totalValueOfPool = 2 * z; + + // lp value = lp tokens * value of pool / total supply + value = (lpTokens * totalValueOfPool) / totalSupply; + } + + /** + * @dev Compute the invariant for a SwapX stable pool. + * This assumed both x and y tokens are to 18 decimals which is checked in the constructor. + * invariant: k = x^3 * y + x * y^3 + * @dev This implementation is copied from SwapX's Pair contract. + * @param x The amount of Wrapped S (wS) tokens in the pool + * @param y The amount of the OS tokens in the pool + * @return k The invariant of the SwapX stable pool + */ + function _invariant(uint256 x, uint256 y) + internal + pure + returns (uint256 k) + { + uint256 _a = (x * y) / PRECISION; + uint256 _b = ((x * x) / PRECISION + (y * y) / PRECISION); + // slither-disable-next-line divide-before-multiply + k = (_a * _b) / PRECISION; + } + + /** + * @dev Checks that the protocol is solvent, protecting from a rogue Strategist / Guardian that can + * keep rebalancing the pool in both directions making the protocol lose a tiny amount of + * funds each time. + * + * Protocol must be at least SOLVENCY_THRESHOLD (99,8 %) backed in order for the rebalances to + * function. + */ + function _solvencyAssert() internal view { + uint256 _totalVaultValue = IVault(vaultAddress).totalValue(); + uint256 _totalOSSupply = IERC20(os).totalSupply(); + + if ( + _totalVaultValue.divPrecisely(_totalOSSupply) < SOLVENCY_THRESHOLD + ) { + revert("Protocol insolvent"); + } + } + + /*************************************** + Approvals + ****************************************/ + + /** + * @notice Approve the spending of all assets by their corresponding pool tokens, + * if for some reason is it necessary. + */ + function safeApproveAllTokens() + external + override + onlyGovernor + nonReentrant + { + _approveBase(); + } + + // solhint-disable-next-line no-unused-vars + function _abstractSetPToken(address _asset, address _pToken) + internal + override + {} + + function _approveBase() internal { + // Approve pool for OS and wS (required for adding liquidity) + // slither-disable-next-line unused-return + IERC20(os).approve(platformAddress, type(uint256).max); + // slither-disable-next-line unused-return + IERC20(ws).approve(platformAddress, type(uint256).max); + + // Approve SwapX gauge contract to transfer SwapX pool LP tokens + // This is needed for deposits of SwapX pool LP tokens into the gauge. + // slither-disable-next-line unused-return + IPair(pool).approve(address(gauge), type(uint256).max); + } +} diff --git a/contracts/contracts/utils/PRBMath.sol b/contracts/contracts/utils/PRBMath.sol new file mode 100644 index 0000000000..090ce51ce4 --- /dev/null +++ b/contracts/contracts/utils/PRBMath.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// Copied from the PRBMath library +// https://github.com/PaulRBerg/prb-math/blob/main/src/Common.sol + +/// @notice Calculates the square root of x using the Babylonian method. +/// +/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method. +/// +/// Notes: +/// - If x is not a perfect square, the result is rounded down. +/// - Credits to OpenZeppelin for the explanations in comments below. +/// +/// @param x The uint256 number for which to calculate the square root. +/// @return result The result as a uint256. +/// @custom:smtchecker abstract-function-nondet +function sqrt(uint256 x) pure returns (uint256 result) { + if (x == 0) { + return 0; + } + + // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x. + // + // We know that the "msb" (most significant bit) of x is a power of 2 such that we have: + // + // $$ + // msb(x) <= x <= 2*msb(x)$ + // $$ + // + // We write $msb(x)$ as $2^k$, and we get: + // + // $$ + // k = log_2(x) + // $$ + // + // Thus, we can write the initial inequality as: + // + // $$ + // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\ + // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\ + // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1} + // $$ + // + // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit. + uint256 xAux = uint256(x); + result = 1; + if (xAux >= 2**128) { + xAux >>= 128; + result <<= 64; + } + if (xAux >= 2**64) { + xAux >>= 64; + result <<= 32; + } + if (xAux >= 2**32) { + xAux >>= 32; + result <<= 16; + } + if (xAux >= 2**16) { + xAux >>= 16; + result <<= 8; + } + if (xAux >= 2**8) { + xAux >>= 8; + result <<= 4; + } + if (xAux >= 2**4) { + xAux >>= 4; + result <<= 2; + } + if (xAux >= 2**2) { + result <<= 1; + } + + // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at + // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision + // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of + // precision into the expected uint128 result. + unchecked { + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + + // If x is not a perfect square, round the result toward zero. + uint256 roundedResult = x / result; + if (result >= roundedResult) { + result = roundedResult; + } + } +} diff --git a/contracts/contracts/vault/OETHBaseVaultAdmin.sol b/contracts/contracts/vault/OETHBaseVaultAdmin.sol index b5020fb5c1..34124d79d9 100644 --- a/contracts/contracts/vault/OETHBaseVaultAdmin.sol +++ b/contracts/contracts/vault/OETHBaseVaultAdmin.sol @@ -1,49 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { VaultAdmin } from "./VaultAdmin.sol"; +import { OETHVaultAdmin } from "./OETHVaultAdmin.sol"; /** * @title OETH Base VaultAdmin Contract * @author Origin Protocol Inc */ -contract OETHBaseVaultAdmin is VaultAdmin { - /** - * @notice Adds a strategy to the mint whitelist. - * Reverts if strategy isn't approved on Vault. - * @param strategyAddr Strategy address - */ - function addStrategyToMintWhitelist(address strategyAddr) - external - onlyGovernor - { - require(strategies[strategyAddr].isSupported, "Strategy not approved"); - - require( - !isMintWhitelistedStrategy[strategyAddr], - "Already whitelisted" - ); - - isMintWhitelistedStrategy[strategyAddr] = true; - - emit StrategyAddedToMintWhitelist(strategistAddr); - } - - /** - * @notice Removes a strategy from the mint whitelist. - * @param strategyAddr Strategy address - */ - function removeStrategyFromMintWhitelist(address strategyAddr) - external - onlyGovernor - { - // Intentionally skipping `strategies.isSupported` check since - // we may wanna remove an address even after removing the strategy - - require(isMintWhitelistedStrategy[strategyAddr], "Not whitelisted"); - - isMintWhitelistedStrategy[strategyAddr] = false; - - emit StrategyRemovedFromMintWhitelist(strategistAddr); - } +contract OETHBaseVaultAdmin is OETHVaultAdmin { + constructor(address _weth) OETHVaultAdmin(_weth) {} } diff --git a/contracts/contracts/vault/OETHBaseVaultCore.sol b/contracts/contracts/vault/OETHBaseVaultCore.sol index 1dd368e413..eb9299e81a 100644 --- a/contracts/contracts/vault/OETHBaseVaultCore.sol +++ b/contracts/contracts/vault/OETHBaseVaultCore.sol @@ -18,48 +18,6 @@ contract OETHBaseVaultCore is OETHVaultCore { constructor(address _weth) OETHVaultCore(_weth) {} - // @inheritdoc VaultCore - function mintForStrategy(uint256 amount) - external - override - whenNotCapitalPaused - { - require( - strategies[msg.sender].isSupported == true, - "Unsupported strategy" - ); - require( - isMintWhitelistedStrategy[msg.sender] == true, - "Not whitelisted strategy" - ); - - emit Mint(msg.sender, amount); - - // Mint matching amount of OTokens - oUSD.mint(msg.sender, amount); - } - - // @inheritdoc VaultCore - function burnForStrategy(uint256 amount) - external - override - whenNotCapitalPaused - { - require( - strategies[msg.sender].isSupported == true, - "Unsupported strategy" - ); - require( - isMintWhitelistedStrategy[msg.sender] == true, - "Not whitelisted strategy" - ); - - emit Redeem(msg.sender, amount); - - // Burn OTokens - oUSD.burn(msg.sender, amount); - } - // @inheritdoc OETHVaultCore function _redeem(uint256 _amount, uint256 _minimumUnitAmount) internal diff --git a/contracts/contracts/vault/OETHVaultAdmin.sol b/contracts/contracts/vault/OETHVaultAdmin.sol index 009a7eec5c..88c61eb095 100644 --- a/contracts/contracts/vault/OETHVaultAdmin.sol +++ b/contracts/contracts/vault/OETHVaultAdmin.sol @@ -19,6 +19,48 @@ contract OETHVaultAdmin is VaultAdmin { constructor(address _weth) { weth = _weth; + + // prevent implementation contract to be governed + _setGovernor(address(1)); + } + + /** + * @notice Adds a strategy to the mint whitelist. + * Reverts if strategy isn't approved on Vault. + * @param strategyAddr Strategy address + */ + function addStrategyToMintWhitelist(address strategyAddr) + external + onlyGovernor + { + require(strategies[strategyAddr].isSupported, "Strategy not approved"); + + require( + !isMintWhitelistedStrategy[strategyAddr], + "Already whitelisted" + ); + + isMintWhitelistedStrategy[strategyAddr] = true; + + emit StrategyAddedToMintWhitelist(strategyAddr); + } + + /** + * @notice Removes a strategy from the mint whitelist. + * @param strategyAddr Strategy address + */ + function removeStrategyFromMintWhitelist(address strategyAddr) + external + onlyGovernor + { + // Intentionally skipping `strategies.isSupported` check since + // we may wanna remove an address even after removing the strategy + + require(isMintWhitelistedStrategy[strategyAddr], "Not whitelisted"); + + isMintWhitelistedStrategy[strategyAddr] = false; + + emit StrategyRemovedFromMintWhitelist(strategyAddr); } /// @dev Simplified version of the deposit function as WETH is the only supported asset. diff --git a/contracts/contracts/vault/OETHVaultCore.sol b/contracts/contracts/vault/OETHVaultCore.sol index d907192232..a03adbfcff 100644 --- a/contracts/contracts/vault/OETHVaultCore.sol +++ b/contracts/contracts/vault/OETHVaultCore.sol @@ -26,6 +26,9 @@ contract OETHVaultCore is VaultCore { constructor(address _weth) { weth = _weth; + + // prevent implementation contract to be governed + _setGovernor(address(1)); } /** @@ -44,6 +47,48 @@ contract OETHVaultCore is VaultCore { require(allAssets[wethAssetIndex] == weth, "Invalid WETH Asset Index"); } + // @inheritdoc VaultCore + function mintForStrategy(uint256 amount) + external + override + whenNotCapitalPaused + { + require( + strategies[msg.sender].isSupported == true, + "Unsupported strategy" + ); + require( + isMintWhitelistedStrategy[msg.sender] == true, + "Not whitelisted strategy" + ); + + emit Mint(msg.sender, amount); + + // Mint matching amount of OTokens + oUSD.mint(msg.sender, amount); + } + + // @inheritdoc VaultCore + function burnForStrategy(uint256 amount) + external + override + whenNotCapitalPaused + { + require( + strategies[msg.sender].isSupported == true, + "Unsupported strategy" + ); + require( + isMintWhitelistedStrategy[msg.sender] == true, + "Not whitelisted strategy" + ); + + emit Redeem(msg.sender, amount); + + // Burn OTokens + oUSD.burn(msg.sender, amount); + } + // @inheritdoc VaultCore // slither-disable-start reentrancy-no-eth function _mint( diff --git a/contracts/contracts/vault/README.md b/contracts/contracts/vault/README.md index b754ce591c..651a49f2d6 100644 --- a/contracts/contracts/vault/README.md +++ b/contracts/contracts/vault/README.md @@ -1,37 +1,61 @@ # Diagrams -## Vault - -### Hierarchy +## Hierarchy ![Vault Core Hierarchy](../../docs/VaultHierarchy.svg) -### Storage - -![Vault Storage](../../docs/VaultStorage.svg) +## OUSD Vault ## Vault Core Squashed ![Vault Core Squashed](../../docs/VaultCoreSquashed.svg) +### Storage + +![Vault Storage](../../docs/VaultStorage.svg) + ## Vault Admin Squashed ![Vault Admin Squashed](../../docs/VaultAdminSquashed.svg) ## OETH Vault -### Hierarchy +## Vault Core Squashed + +![OETH Vault Core Squashed](../../docs/OETHVaultCoreSquashed.svg) + +## Vault Admin Squashed -![OETH Vault Core Hierarchy](../../docs/OETHVaultHierarchy.svg) +![OETH Vault Admin Squashed](../../docs/OETHVaultAdminSquashed.svg) ### Storage ![OETH Vault Storage](../../docs/OETHVaultStorage.svg) -## OETH Vault Core Squashed +## Base OETH Vault -![OETH Vault Core Squashed](../../docs/OETHVaultCoreSquashed.svg) +## Vault Core Squashed -## OETH Vault Admin Squashed +![OETH Vault Core Squashed](../../docs/OETHBaseVaultCoreSquashed.svg) -![OETH Vault Admin Squashed](../../docs/OETHVaultAdminSquashed.svg) +## Vault Admin Squashed + +![OETH Vault Admin Squashed](../../docs/OETHBaseVaultAdminSquashed.svg) + +### Storage + +![OETH Vault Storage](../../docs/OETHBaseVaultStorage.svg) + +## Origin Sonic (OS) Vault + +## Vault Core Squashed + +![Sonic Vault Core Squashed](../../docs/OSonicVaultCoreSquashed.svg) + +## Vault Admin Squashed + +![Sonic Vault Admin Squashed](../../docs/OSonicVaultAdminSquashed.svg) + +### Storage + +![Sonic Vault Storage](../../docs/OSonicVaultStorage.svg) \ No newline at end of file diff --git a/contracts/deploy/base/000_mock.js b/contracts/deploy/base/000_mock.js index af6b617cdc..d06adc7ab5 100644 --- a/contracts/deploy/base/000_mock.js +++ b/contracts/deploy/base/000_mock.js @@ -87,7 +87,9 @@ const deployCore = async () => { const dOETHbVaultCore = await deployWithConfirmation("OETHBaseVaultCore", [ cWETH.address, ]); - const dOETHbVaultAdmin = await deployWithConfirmation("OETHBaseVaultAdmin"); + const dOETHbVaultAdmin = await deployWithConfirmation("OETHBaseVaultAdmin", [ + cWETH.address, + ]); // Get contract instances const cOETHb = await ethers.getContractAt("OETHBase", cOETHbProxy.address); diff --git a/contracts/deploy/base/025_base_curve_amo.js b/contracts/deploy/base/025_base_curve_amo.js index 3e8832d518..456931688b 100644 --- a/contracts/deploy/base/025_base_curve_amo.js +++ b/contracts/deploy/base/025_base_curve_amo.js @@ -40,6 +40,8 @@ module.exports = deployOnBase( addresses.base.WETH, addresses.base.OETHb_WETH.gauge, addresses.base.childLiquidityGaugeFactory, + 1, // SuperOETH is coin 1 of the Curve WETH/SuperOETH pool + 0, // WETH is coin 0 of the Curve WETH/SuperOETH pool ] ); const cOETHBaseCurveAMO = await ethers.getContractAt( diff --git a/contracts/deploy/base/027_base_curve_amo_upgrade.js b/contracts/deploy/base/027_base_curve_amo_upgrade.js new file mode 100644 index 0000000000..fa838d7642 --- /dev/null +++ b/contracts/deploy/base/027_base_curve_amo_upgrade.js @@ -0,0 +1,69 @@ +const { deployOnBase } = require("../../utils/deploy-l2"); +const { deployWithConfirmation } = require("../../utils/deploy"); +const addresses = require("../../utils/addresses"); + +module.exports = deployOnBase( + { + deployName: "027_base_curve_amo_upgrade", + }, + async ({ ethers }) => { + // Get the SuperOETH, Vault and Curve AMO contracts + const cOETHbProxy = await ethers.getContract("OETHBaseProxy"); + const cOETHbVaultProxy = await ethers.getContract("OETHBaseVaultProxy"); + const cOETHbVault = await ethers.getContractAt( + "IVault", + cOETHbVaultProxy.address + ); + const cOETHBaseCurveAMOProxy = await ethers.getContract( + "OETHBaseCurveAMOProxy" + ); + + // Deploy Base Curve AMO implementation + const dOETHBaseCurveAMOImpl = await deployWithConfirmation( + "BaseCurveAMOStrategy", + [ + [addresses.base.OETHb_WETH.pool, cOETHbVaultProxy.address], + cOETHbProxy.address, + addresses.base.WETH, + addresses.base.OETHb_WETH.gauge, + addresses.base.childLiquidityGaugeFactory, + 1, // SuperOETH is coin 1 of the Curve WETH/SuperOETH pool + 0, // WETH is coin 0 of the Curve WETH/SuperOETH pool + ] + ); + + // Deploy new Vault Core implementation + const dOETHbVaultCore = await deployWithConfirmation("OETHBaseVaultCore", [ + addresses.base.WETH, + ]); + + // Deploy new Vault Admin implementation + const dOETHbVaultAdmin = await deployWithConfirmation( + "OETHBaseVaultAdmin", + [addresses.base.WETH] + ); + + return { + actions: [ + // 1. Upgrade the Base Curve AMO Strategy implementation + { + contract: cOETHBaseCurveAMOProxy, + signature: "upgradeTo(address)", + args: [dOETHBaseCurveAMOImpl.address], + }, + // 2. Upgrade VaultCore + { + contract: cOETHbVaultProxy, + signature: "upgradeTo(address)", + args: [dOETHbVaultCore.address], + }, + // 3. Upgrade VaultAdmin + { + contract: cOETHbVault, + signature: "setAdminImpl(address)", + args: [dOETHbVaultAdmin.address], + }, + ], + }; + } +); diff --git a/contracts/deploy/mainnet/126_oeth_vault_upgrade.js b/contracts/deploy/mainnet/126_oeth_vault_upgrade.js new file mode 100644 index 0000000000..9009cafaa4 --- /dev/null +++ b/contracts/deploy/mainnet/126_oeth_vault_upgrade.js @@ -0,0 +1,59 @@ +const addresses = require("../../utils/addresses"); +const { deploymentWithGovernanceProposal } = require("../../utils/deploy"); + +module.exports = deploymentWithGovernanceProposal( + { + deployName: "126_oeth_vault_upgrade", + forceDeploy: false, + //forceSkip: true, + reduceQueueTime: true, + deployerIsProposer: false, + // proposalId: "", + }, + async ({ deployWithConfirmation }) => { + // Deployer Actions + // ---------------- + + // 1. Deploy new OETH Vault Core and Admin implementations + const dVaultCore = await deployWithConfirmation("OETHVaultCore", [ + addresses.mainnet.WETH, + ]); + const dVaultAdmin = await deployWithConfirmation("OETHVaultAdmin", [ + addresses.mainnet.WETH, + ]); + + // 2. Connect to the OETH Vault as its governor via the proxy + const cVaultProxy = await ethers.getContract("OETHVaultProxy"); + const cVault = await ethers.getContractAt("IVault", cVaultProxy.address); + + const cCurveAMOStrategyProxy = await ethers.getContract( + "ConvexEthMetaStrategyProxy" + ); + + // Governance Actions + // ---------------- + return { + name: "Upgrade OETH Vault", + actions: [ + // 1. Upgrade the OETH Vault proxy to the new core vault implementation + { + contract: cVaultProxy, + signature: "upgradeTo(address)", + args: [dVaultCore.address], + }, + // 2. Set OETH Vault proxy to the new admin vault implementation + { + contract: cVault, + signature: "setAdminImpl(address)", + args: [dVaultAdmin.address], + }, + // 3. Add the Curve AMO as a whitelisted address + { + contract: cVault, + signature: "addStrategyToMintWhitelist(address)", + args: [cCurveAMOStrategyProxy.address], + }, + ], + }; + } +); diff --git a/contracts/deploy/sonic/009_harvester.js b/contracts/deploy/sonic/009_harvester.js deleted file mode 100644 index 6b78a4ce55..0000000000 --- a/contracts/deploy/sonic/009_harvester.js +++ /dev/null @@ -1,57 +0,0 @@ -const addresses = require("../../utils/addresses.js"); -const { deployOnSonic } = require("../../utils/deploy-l2.js"); -const { - deployWithConfirmation, - withConfirmation, -} = require("../../utils/deploy.js"); - -module.exports = deployOnSonic( - { - deployName: "009_harvester", - }, - async ({ ethers }) => { - const { deployerAddr, strategistAddr } = await getNamedAccounts(); - - const sDeployer = await ethers.provider.getSigner(deployerAddr); - await deployWithConfirmation("OSonicHarvesterProxy"); - - await deployWithConfirmation("OETHHarvesterSimple", [addresses.sonic.wS]); - const dHarvester = await ethers.getContract("OETHHarvesterSimple"); - - const cHarvesterProxy = await ethers.getContract("OSonicHarvesterProxy"); - const cHarvester = await ethers.getContractAt( - "OETHHarvesterSimple", - cHarvesterProxy.address - ); - - const cDripperProxy = await ethers.getContract("OSonicDripperProxy"); - - // const cStakingStrategyProxy = await ethers.getContract("SonicStakingStrategyProxy"); - - const initSonicStakingStrategy = cHarvester.interface.encodeFunctionData( - "initialize(address,address,address)", - [addresses.sonic.timelock, strategistAddr, cDripperProxy.address] - ); - - // prettier-ignore - await withConfirmation( - cHarvesterProxy - .connect(sDeployer)["initialize(address,address,bytes)"]( - dHarvester.address, - addresses.sonic.timelock, - initSonicStakingStrategy - ) - ); - - return { - actions: [ - // TODO: Enable for Curve AMO after it has been deployed - // { - // contract: cHarvesterProxy, - // signature: "setSupportedStrategy(address,bool)", - // args: [cStakingStrategyProxy.address, true], - // }, - ], - }; - } -); diff --git a/contracts/deploy/sonic/009_swapx_amo.js b/contracts/deploy/sonic/009_swapx_amo.js new file mode 100644 index 0000000000..25d648b087 --- /dev/null +++ b/contracts/deploy/sonic/009_swapx_amo.js @@ -0,0 +1,145 @@ +const { deployOnSonic } = require("../../utils/deploy-l2"); +const { + deployWithConfirmation, + withConfirmation, +} = require("../../utils/deploy"); +const addresses = require("../../utils/addresses"); + +module.exports = deployOnSonic( + { + deployName: "009_swapx_amo", + }, + async ({ ethers }) => { + const { deployerAddr, strategistAddr } = await getNamedAccounts(); + const sDeployer = await ethers.provider.getSigner(deployerAddr); + + // Deploy a new Vault Core implementation + const dOSonicVaultCore = await deployWithConfirmation("OSonicVaultCore", [ + addresses.sonic.wS, + ]); + console.log(`Deployed Vault Core to ${dOSonicVaultCore.address}`); + + // Deploy a new Vault Admin implementation + const dOSonicVaultAdmin = await deployWithConfirmation("OSonicVaultAdmin", [ + addresses.sonic.wS, + ]); + console.log( + `Deployed Origin Sonic Vault Admin to ${dOSonicVaultAdmin.address}` + ); + + // Deploy the Harvester proxy + await deployWithConfirmation("OSonicHarvesterProxy"); + + // Deploy the Harvester implementation + await deployWithConfirmation("OETHHarvesterSimple", [addresses.sonic.wS]); + const dHarvester = await ethers.getContract("OETHHarvesterSimple"); + + const cHarvesterProxy = await ethers.getContract("OSonicHarvesterProxy"); + const cHarvester = await ethers.getContractAt( + "OETHHarvesterSimple", + cHarvesterProxy.address + ); + + const cDripperProxy = await ethers.getContract("OSonicDripperProxy"); + + const initSonicStakingStrategy = cHarvester.interface.encodeFunctionData( + "initialize(address,address,address)", + [addresses.sonic.timelock, strategistAddr, cDripperProxy.address] + ); + + // Initialize the Harvester + // prettier-ignore + await withConfirmation( + cHarvesterProxy + .connect(sDeployer)["initialize(address,address,bytes)"]( + dHarvester.address, + addresses.sonic.timelock, + initSonicStakingStrategy + ) + ); + + // Deploy Sonic SwapX AMO Strategy proxy + const cOSonicProxy = await ethers.getContract("OSonicProxy"); + const cOSonicVaultProxy = await ethers.getContract("OSonicVaultProxy"); + const cOSonicVaultAdmin = await ethers.getContractAt( + "OSonicVaultAdmin", + cOSonicVaultProxy.address + ); + const dSonicSwapXAMOStrategyProxy = await deployWithConfirmation( + "SonicSwapXAMOStrategyProxy", + [] + ); + const cSonicSwapXAMOStrategyProxy = await ethers.getContract( + "SonicSwapXAMOStrategyProxy" + ); + + // Deploy Sonic SwapX AMO Strategy implementation + const dSonicSwapXAMOStrategy = await deployWithConfirmation( + "SonicSwapXAMOStrategy", + [ + [addresses.sonic.SwapXWSOS.pool, cOSonicVaultProxy.address], + cOSonicProxy.address, + addresses.sonic.wS, + addresses.sonic.SwapXWSOS.gauge, + ] + ); + const cSonicSwapXAMOStrategy = await ethers.getContractAt( + "SonicSwapXAMOStrategy", + dSonicSwapXAMOStrategyProxy.address + ); + // Initialize Sonic Curve AMO Strategy implementation + const initData = cSonicSwapXAMOStrategy.interface.encodeFunctionData( + "initialize(address[])", + [[addresses.sonic.SWPx]] + ); + await withConfirmation( + // prettier-ignore + cSonicSwapXAMOStrategyProxy + .connect(sDeployer)["initialize(address,address,bytes)"]( + dSonicSwapXAMOStrategy.address, + addresses.sonic.timelock, + initData + ) + ); + return { + actions: [ + // 1. Upgrade Vault proxy to new VaultCore + { + contract: cOSonicVaultProxy, + signature: "upgradeTo(address)", + args: [dOSonicVaultCore.address], + }, + // 2. Upgrade the VaultAdmin + { + contract: cOSonicVaultAdmin, + signature: "setAdminImpl(address)", + args: [dOSonicVaultAdmin.address], + }, + // 3. Approve new strategy on the Vault + { + contract: cOSonicVaultAdmin, + signature: "approveStrategy(address)", + args: [cSonicSwapXAMOStrategyProxy.address], + }, + // 4. Add strategy to mint whitelist + { + contract: cOSonicVaultAdmin, + signature: "addStrategyToMintWhitelist(address)", + args: [cSonicSwapXAMOStrategyProxy.address], + }, + // 5. Enable for SwapX AMO after it has been deployed + { + contract: cHarvester, + signature: "setSupportedStrategy(address,bool)", + args: [cSonicSwapXAMOStrategyProxy.address, true], + }, + // 6. Set the Harvester on the SwapX AMO strategy + { + contract: cSonicSwapXAMOStrategy, + signature: "setHarvesterAddress(address)", + args: [cHarvesterProxy.address], + }, + ], + }; + } +); diff --git a/contracts/deploy/sonic/014_curve_amo.js b/contracts/deploy/sonic/014_curve_amo.js new file mode 100644 index 0000000000..908665cca2 --- /dev/null +++ b/contracts/deploy/sonic/014_curve_amo.js @@ -0,0 +1,102 @@ +const { deployOnSonic } = require("../../utils/deploy-l2"); +const { + deployWithConfirmation, + withConfirmation, +} = require("../../utils/deploy"); +const addresses = require("../../utils/addresses"); +const { oethUnits } = require("../../test/helpers"); + +module.exports = deployOnSonic( + { + deployName: "014_curve_amo", + }, + async ({ ethers }) => { + const { deployerAddr } = await getNamedAccounts(); + const sDeployer = await ethers.provider.getSigner(deployerAddr); + + // Get exiting contracts + const cOSonicProxy = await ethers.getContract("OSonicProxy"); + const cOSonicVaultProxy = await ethers.getContract("OSonicVaultProxy"); + const cOSonicVaultAdmin = await ethers.getContractAt( + "OSonicVaultAdmin", + cOSonicVaultProxy.address + ); + const cHarvesterProxy = await ethers.getContract("OSonicHarvesterProxy"); + const cHarvester = await ethers.getContractAt( + "OETHHarvesterSimple", + cHarvesterProxy.address + ); + + // Deploy Sonic Curve AMO Strategy proxy + const dSonicCurveAMOStrategyProxy = await deployWithConfirmation( + "SonicCurveAMOStrategyProxy", + [] + ); + + const cSonicCurveAMOStrategyProxy = await ethers.getContract( + "SonicCurveAMOStrategyProxy" + ); + + // Deploy Sonic Curve AMO Strategy implementation + const dSonicCurveAMOStrategy = await deployWithConfirmation( + "SonicCurveAMOStrategy", + [ + [addresses.sonic.WS_OS.pool, cOSonicVaultProxy.address], + cOSonicProxy.address, + addresses.sonic.wS, + addresses.sonic.WS_OS.gauge, + addresses.sonic.childLiquidityGaugeFactory, + 0, // The OToken (OS for Sonic) is coin 0 of the Curve OS/wS pool + 1, // The WETH token (wS for Sonic) is coin 1 of the Curve OS/wS pool + ] + ); + const cSonicCurveAMOStrategy = await ethers.getContractAt( + "SonicCurveAMOStrategy", + dSonicCurveAMOStrategyProxy.address + ); + + // Initialize Sonic Curve AMO Strategy implementation + const initData = cSonicCurveAMOStrategy.interface.encodeFunctionData( + "initialize(address[],uint256)", + [[addresses.sonic.CRV], oethUnits("0.002")] + ); + await withConfirmation( + // prettier-ignore + cSonicCurveAMOStrategyProxy + .connect(sDeployer)["initialize(address,address,bytes)"]( + dSonicCurveAMOStrategy.address, + addresses.sonic.timelock, + initData + ) + ); + + return { + actions: [ + // 3. Approve strategy on vault + { + contract: cOSonicVaultAdmin, + signature: "approveStrategy(address)", + args: [cSonicCurveAMOStrategyProxy.address], + }, + // 4. Add strategy to mint whitelist + { + contract: cOSonicVaultAdmin, + signature: "addStrategyToMintWhitelist(address)", + args: [cSonicCurveAMOStrategyProxy.address], + }, + // 5. Enable for Curve AMO after it has been deployed + { + contract: cHarvester, + signature: "setSupportedStrategy(address,bool)", + args: [cSonicCurveAMOStrategyProxy.address, true], + }, + // 6. Set the Harvester on the Curve AMO strategy + { + contract: cSonicCurveAMOStrategy, + signature: "setHarvesterAddress(address)", + args: [cHarvesterProxy.address], + }, + ], + }; + } +); diff --git a/contracts/docs/DripperHierarchy.svg b/contracts/docs/DripperHierarchy.svg index 0f27419ec5..98caed5df1 100644 --- a/contracts/docs/DripperHierarchy.svg +++ b/contracts/docs/DripperHierarchy.svg @@ -4,136 +4,30 @@ - - + + UmlClassDiagram - - + + -20 - -Governable -../contracts/governance/Governable.sol +21 + +Governable +../contracts/governance/Governable.sol - + -30 +31 Dripper ../contracts/harvest/Dripper.sol - + -30->20 - - - - - -61 - -<<Interface>> -IVault -../contracts/interfaces/IVault.sol - - - -30->61 - - - - - -710 - -<<Interface>> -IERC20 -../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol - - - -30->710 - - - - - -239 - -VaultStorage -../contracts/vault/VaultStorage.sol - - - -61->239 - - - - - -215 - -OUSD -../contracts/token/OUSD.sol - - - -215->20 - - - - - -224 - -<<Abstract>> -Initializable -../contracts/utils/Initializable.sol - - - -215->224 - - - - - -227 - -<<Abstract>> -InitializableERC20Detailed -../contracts/utils/InitializableERC20Detailed.sol - - - -215->227 - - - - - -227->710 - - - - - -239->20 - - - - - -239->215 - - - - - -239->224 - - +31->21 + + diff --git a/contracts/docs/HarvesterHierarchy.svg b/contracts/docs/HarvesterHierarchy.svg index c4334d5fd1..1791e1aa00 100644 --- a/contracts/docs/HarvesterHierarchy.svg +++ b/contracts/docs/HarvesterHierarchy.svg @@ -4,234 +4,44 @@ - - + + UmlClassDiagram - - + + -20 - -Governable -../contracts/governance/Governable.sol +21 + +Governable +../contracts/governance/Governable.sol - + -26 - -<<Abstract>> -AbstractHarvester -../contracts/harvest/AbstractHarvester.sol +27 + +<<Abstract>> +AbstractHarvester +../contracts/harvest/AbstractHarvester.sol - + -26->20 - - +27->21 + + - - -51 - -<<Interface>> -IOracle -../contracts/interfaces/IOracle.sol - - - -26->51 - - - - - -57 - -<<Interface>> -IStrategy -../contracts/interfaces/IStrategy.sol - - - -26->57 - - - - - -61 - -<<Interface>> -IVault -../contracts/interfaces/IVault.sol - - - -26->61 - - - - - -249 - -<<Interface>> -IBalancerVault -../contracts/interfaces/balancer/IBalancerVault.sol - - - -26->249 - - - - - -280 - -<<Interface>> -IUniswapV2Router -../contracts/interfaces/uniswap/IUniswapV2Router02.sol - - - -26->280 - - - - - -281 - -<<Interface>> -IUniswapV3Router -../contracts/interfaces/uniswap/IUniswapV3Router.sol - - - -26->281 - - - - - -199 - -<<Interface>> -ICurvePool -../contracts/strategies/ICurvePool.sol - - - -26->199 - - - - - -710 - -<<Interface>> -IERC20 -../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol - - - -26->710 - - - - + -32 - -Harvester -../contracts/harvest/Harvester.sol - - - -32->26 - - - - - -239 - -VaultStorage -../contracts/vault/VaultStorage.sol - - - -61->239 - - - - - -215 - -OUSD -../contracts/token/OUSD.sol - - - -215->20 - - - - - -224 - -<<Abstract>> -Initializable -../contracts/utils/Initializable.sol +34 + +Harvester +../contracts/harvest/Harvester.sol - - -215->224 - - - - - -227 - -<<Abstract>> -InitializableERC20Detailed -../contracts/utils/InitializableERC20Detailed.sol - - - -215->227 - - - - - -227->710 - - - - - -239->20 - - - - - -239->215 - - - - - -239->224 - - + + +34->27 + + diff --git a/contracts/docs/OETHBaseHarvesterHierarchy.svg b/contracts/docs/OETHBaseHarvesterHierarchy.svg new file mode 100644 index 0000000000..83e0824f5f --- /dev/null +++ b/contracts/docs/OETHBaseHarvesterHierarchy.svg @@ -0,0 +1,33 @@ + + + + + + +UmlClassDiagram + + + +21 + +Governable +../contracts/governance/Governable.sol + + + +35 + +OETHBaseHarvester +../contracts/harvest/OETHBaseHarvester.sol + + + +35->21 + + + + + diff --git a/contracts/docs/OETHBaseHarvesterSquashed.svg b/contracts/docs/OETHBaseHarvesterSquashed.svg new file mode 100644 index 0000000000..cf200cb47c --- /dev/null +++ b/contracts/docs/OETHBaseHarvesterSquashed.svg @@ -0,0 +1,63 @@ + + + + + + +UmlClassDiagram + + + +35 + +OETHBaseHarvester +../contracts/harvest/OETHBaseHarvester.sol + +Private: +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   vault: IVault <<OETHBaseHarvester>> +   amoStrategy: IStrategy <<OETHBaseHarvester>> +   aero: IERC20 <<OETHBaseHarvester>> +   weth: IERC20 <<OETHBaseHarvester>> +   swapRouter: ISwapRouter <<OETHBaseHarvester>> +   operatorAddr: address <<OETHBaseHarvester>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _doSwap(aeroToSwap: uint256, minWETHExpected: uint256) <<OETHBaseHarvester>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    setOperatorAddr(_operatorAddr: address) <<onlyGovernor>> <<OETHBaseHarvester>> +    harvest() <<onlyGovernorOrStrategistOrOperator>> <<OETHBaseHarvester>> +    harvestAndSwap(aeroToSwap: uint256, minWETHExpected: uint256, feeBps: uint256, sendYieldToDripper: bool) <<onlyGovernorOrStrategist>> <<OETHBaseHarvester>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<OETHBaseHarvester>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> RewardTokenSwapped(rewardToken: address, swappedInto: address, swapPlatform: uint8, amountIn: uint256, amountOut: uint256) <<OETHBaseHarvester>> +    <<event>> OperatorChanged(oldOperator: address, newOperator: address) <<OETHBaseHarvester>> +    <<event>> YieldSent(recipient: address, yield: uint256, fee: uint256) <<OETHBaseHarvester>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    <<modifier>> onlyGovernorOrStrategist() <<OETHBaseHarvester>> +    <<modifier>> onlyGovernorOrStrategistOrOperator() <<OETHBaseHarvester>> +    constructor() <<Governable>> +    governor(): address <<Governable>> +    isGovernor(): bool <<Governable>> +    constructor(_vault: address, _amoStrategy: address, _aero: address, _weth: address, _swapRouter: address) <<OETHBaseHarvester>> + + + diff --git a/contracts/docs/OETHBaseHarvesterStorage.svg b/contracts/docs/OETHBaseHarvesterStorage.svg new file mode 100644 index 0000000000..efc29089ec --- /dev/null +++ b/contracts/docs/OETHBaseHarvesterStorage.svg @@ -0,0 +1,29 @@ + + + + + + +StorageDiagram + + + +1 + +OETHBaseHarvester <<Contract>> + +slot + +0 + +type: <inherited contract>.variable (bytes) + +unallocated (12) + +address: operatorAddr (20) + + + diff --git a/contracts/docs/OETHBaseHierarchy.svg b/contracts/docs/OETHBaseHierarchy.svg deleted file mode 100644 index 96c9e666f7..0000000000 --- a/contracts/docs/OETHBaseHierarchy.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - -UmlClassDiagram - - - -21 - -Governable -../contracts/governance/Governable.sol - - - -243 - -OETHBase -../contracts/token/OETHBase.sol - - - -245 - -OUSD -../contracts/token/OUSD.sol - - - -243->245 - - - - - -245->21 - - - - - diff --git a/contracts/docs/OETHBaseVaultAdminSquashed.svg b/contracts/docs/OETHBaseVaultAdminSquashed.svg new file mode 100644 index 0000000000..a4f23c6a14 --- /dev/null +++ b/contracts/docs/OETHBaseVaultAdminSquashed.svg @@ -0,0 +1,167 @@ + + + + + + +UmlClassDiagram + + + +282 + +OETHBaseVaultAdmin +../contracts/vault/OETHBaseVaultAdmin.sol + +Private: +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   _deprecated_rebaseHooksAddr: address <<VaultStorage>> +   _deprecated_uniswapAddr: address <<VaultStorage>> +   _deprecated_swapTokens: address[] <<VaultStorage>> +   __gap: uint256[44] <<VaultStorage>> +Internal: +   assets: mapping(address=>Asset) <<VaultStorage>> +   allAssets: address[] <<VaultStorage>> +   strategies: mapping(address=>Strategy) <<VaultStorage>> +   allStrategies: address[] <<VaultStorage>> +   oUSD: OUSD <<VaultStorage>> +   swapConfig: SwapConfig <<VaultStorage>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   priceProvider: address <<VaultStorage>> +   rebasePaused: bool <<VaultStorage>> +   capitalPaused: bool <<VaultStorage>> +   redeemFeeBps: uint256 <<VaultStorage>> +   vaultBuffer: uint256 <<VaultStorage>> +   autoAllocateThreshold: uint256 <<VaultStorage>> +   rebaseThreshold: uint256 <<VaultStorage>> +   adminImplPosition: bytes32 <<VaultStorage>> +   strategistAddr: address <<VaultStorage>> +   assetDefaultStrategies: mapping(address=>address) <<VaultStorage>> +   maxSupplyDiff: uint256 <<VaultStorage>> +   trusteeAddress: address <<VaultStorage>> +   trusteeFeeBps: uint256 <<VaultStorage>> +   MINT_MINIMUM_UNIT_PRICE: uint256 <<VaultStorage>> +   ousdMetaStrategy: address <<VaultStorage>> +   netOusdMintedForStrategy: int256 <<VaultStorage>> +   netOusdMintForStrategyThreshold: uint256 <<VaultStorage>> +   MIN_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   MAX_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   isMintWhitelistedStrategy: mapping(address=>bool) <<VaultStorage>> +   dripper: address <<VaultStorage>> +   withdrawalQueueMetadata: WithdrawalQueueMetadata <<VaultStorage>> +   withdrawalRequests: mapping(uint256=>WithdrawalRequest) <<VaultStorage>> +   withdrawalClaimDelay: uint256 <<VaultStorage>> +   weth: address <<OETHVaultAdmin>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _swapCollateral(address, address, uint256, uint256, bytes): uint256 <<OETHVaultAdmin>> +    _depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> +    _withdrawFromStrategy(_recipient: address, _strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> +    _withdrawAllFromStrategy(_strategyAddr: address) <<OETHVaultAdmin>> +    _withdrawAllFromStrategies() <<OETHVaultAdmin>> +    _cacheDecimals(token: address) <<VaultAdmin>> +    _wethAvailable(): (wethAvailable: uint256) <<OETHVaultAdmin>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    setAdminImpl(newImpl: address) <<onlyGovernor>> <<VaultStorage>> +    setPriceProvider(_priceProvider: address) <<onlyGovernor>> <<VaultAdmin>> +    setRedeemFeeBps(_redeemFeeBps: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setVaultBuffer(_vaultBuffer: uint256) <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    setAutoAllocateThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setRebaseThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setStrategistAddr(_address: address) <<onlyGovernor>> <<VaultAdmin>> +    setAssetDefaultStrategy(_asset: address, _strategy: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    setNetOusdMintForStrategyThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setDripper(_dripper: address) <<onlyGovernor>> <<VaultAdmin>> +    setWithdrawalClaimDelay(_delay: uint256) <<onlyGovernor>> <<VaultAdmin>> +    swapCollateral(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _minToAssetAmount: uint256, _data: bytes): (toAssetAmount: uint256) <<nonReentrant, onlyGovernorOrStrategist>> <<VaultAdmin>> +    setSwapper(_swapperAddr: address) <<onlyGovernor>> <<VaultAdmin>> +    swapper(): (swapper_: address) <<VaultAdmin>> +    setSwapAllowedUndervalue(_basis: uint16) <<onlyGovernor>> <<VaultAdmin>> +    allowedSwapUndervalue(): (value: uint256) <<VaultAdmin>> +    setOracleSlippage(_asset: address, _allowedOracleSlippageBps: uint16) <<onlyGovernor>> <<VaultAdmin>> +    supportAsset(_asset: address, _unitConversion: uint8) <<onlyGovernor>> <<VaultAdmin>> +    removeAsset(_asset: address) <<onlyGovernor>> <<VaultAdmin>> +    cacheDecimals(_asset: address) <<onlyGovernor>> <<VaultAdmin>> +    approveStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> +    removeStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> +    depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> +    withdrawFromStrategy(_strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> +    setMaxSupplyDiff(_maxSupplyDiff: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setTrusteeAddress(_address: address) <<onlyGovernor>> <<VaultAdmin>> +    setTrusteeFeeBps(_basis: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setOusdMetaStrategy(_ousdMetaStrategy: address) <<onlyGovernor>> <<VaultAdmin>> +    pauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    unpauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    pauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    unpauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<VaultAdmin>> +    withdrawAllFromStrategy(_strategyAddr: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    withdrawAllFromStrategies() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    addStrategyToMintWhitelist(strategyAddr: address) <<onlyGovernor>> <<OETHVaultAdmin>> +    removeStrategyFromMintWhitelist(strategyAddr: address) <<onlyGovernor>> <<OETHVaultAdmin>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> AssetSupported(_asset: address) <<VaultStorage>> +    <<event>> AssetRemoved(_asset: address) <<VaultStorage>> +    <<event>> AssetDefaultStrategyUpdated(_asset: address, _strategy: address) <<VaultStorage>> +    <<event>> AssetAllocated(_asset: address, _strategy: address, _amount: uint256) <<VaultStorage>> +    <<event>> StrategyApproved(_addr: address) <<VaultStorage>> +    <<event>> StrategyRemoved(_addr: address) <<VaultStorage>> +    <<event>> Mint(_addr: address, _value: uint256) <<VaultStorage>> +    <<event>> Redeem(_addr: address, _value: uint256) <<VaultStorage>> +    <<event>> CapitalPaused() <<VaultStorage>> +    <<event>> CapitalUnpaused() <<VaultStorage>> +    <<event>> RebasePaused() <<VaultStorage>> +    <<event>> RebaseUnpaused() <<VaultStorage>> +    <<event>> VaultBufferUpdated(_vaultBuffer: uint256) <<VaultStorage>> +    <<event>> OusdMetaStrategyUpdated(_ousdMetaStrategy: address) <<VaultStorage>> +    <<event>> RedeemFeeUpdated(_redeemFeeBps: uint256) <<VaultStorage>> +    <<event>> PriceProviderUpdated(_priceProvider: address) <<VaultStorage>> +    <<event>> AllocateThresholdUpdated(_threshold: uint256) <<VaultStorage>> +    <<event>> RebaseThresholdUpdated(_threshold: uint256) <<VaultStorage>> +    <<event>> StrategistUpdated(_address: address) <<VaultStorage>> +    <<event>> MaxSupplyDiffChanged(maxSupplyDiff: uint256) <<VaultStorage>> +    <<event>> YieldDistribution(_to: address, _yield: uint256, _fee: uint256) <<VaultStorage>> +    <<event>> TrusteeFeeBpsChanged(_basis: uint256) <<VaultStorage>> +    <<event>> TrusteeAddressChanged(_address: address) <<VaultStorage>> +    <<event>> NetOusdMintForStrategyThresholdChanged(_threshold: uint256) <<VaultStorage>> +    <<event>> SwapperChanged(_address: address) <<VaultStorage>> +    <<event>> SwapAllowedUndervalueChanged(_basis: uint256) <<VaultStorage>> +    <<event>> SwapSlippageChanged(_asset: address, _basis: uint256) <<VaultStorage>> +    <<event>> Swapped(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _toAssetAmount: uint256) <<VaultStorage>> +    <<event>> StrategyAddedToMintWhitelist(strategy: address) <<VaultStorage>> +    <<event>> StrategyRemovedFromMintWhitelist(strategy: address) <<VaultStorage>> +    <<event>> DripperChanged(_dripper: address) <<VaultStorage>> +    <<event>> WithdrawalRequested(_withdrawer: address, _requestId: uint256, _amount: uint256, _queued: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimed(_withdrawer: address, _requestId: uint256, _amount: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimable(_claimable: uint256, _newClaimable: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimDelayUpdated(_newDelay: uint256) <<VaultStorage>> +    <<modifier>> initializer() <<Initializable>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    <<modifier>> onlyGovernorOrStrategist() <<VaultAdmin>> +    constructor() <<Governable>> +    governor(): address <<Governable>> +    isGovernor(): bool <<Governable>> +    constructor(_weth: address) <<OETHBaseVaultAdmin>> + + + diff --git a/contracts/docs/OETHBaseVaultCoreSquashed.svg b/contracts/docs/OETHBaseVaultCoreSquashed.svg new file mode 100644 index 0000000000..194fb658f8 --- /dev/null +++ b/contracts/docs/OETHBaseVaultCoreSquashed.svg @@ -0,0 +1,173 @@ + + + + + + +UmlClassDiagram + + + +283 + +OETHBaseVaultCore +../contracts/vault/OETHBaseVaultCore.sol + +Private: +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   _deprecated_rebaseHooksAddr: address <<VaultStorage>> +   _deprecated_uniswapAddr: address <<VaultStorage>> +   _deprecated_swapTokens: address[] <<VaultStorage>> +   __gap: uint256[44] <<VaultStorage>> +   __gap: uint256[50] <<OETHVaultCore>> +Internal: +   assets: mapping(address=>Asset) <<VaultStorage>> +   allAssets: address[] <<VaultStorage>> +   strategies: mapping(address=>Strategy) <<VaultStorage>> +   allStrategies: address[] <<VaultStorage>> +   oUSD: OUSD <<VaultStorage>> +   swapConfig: SwapConfig <<VaultStorage>> +   MAX_INT: uint256 <<VaultCore>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   priceProvider: address <<VaultStorage>> +   rebasePaused: bool <<VaultStorage>> +   capitalPaused: bool <<VaultStorage>> +   redeemFeeBps: uint256 <<VaultStorage>> +   vaultBuffer: uint256 <<VaultStorage>> +   autoAllocateThreshold: uint256 <<VaultStorage>> +   rebaseThreshold: uint256 <<VaultStorage>> +   adminImplPosition: bytes32 <<VaultStorage>> +   strategistAddr: address <<VaultStorage>> +   assetDefaultStrategies: mapping(address=>address) <<VaultStorage>> +   maxSupplyDiff: uint256 <<VaultStorage>> +   trusteeAddress: address <<VaultStorage>> +   trusteeFeeBps: uint256 <<VaultStorage>> +   MINT_MINIMUM_UNIT_PRICE: uint256 <<VaultStorage>> +   ousdMetaStrategy: address <<VaultStorage>> +   netOusdMintedForStrategy: int256 <<VaultStorage>> +   netOusdMintForStrategyThreshold: uint256 <<VaultStorage>> +   MIN_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   MAX_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   isMintWhitelistedStrategy: mapping(address=>bool) <<VaultStorage>> +   dripper: address <<VaultStorage>> +   withdrawalQueueMetadata: WithdrawalQueueMetadata <<VaultStorage>> +   withdrawalRequests: mapping(uint256=>WithdrawalRequest) <<VaultStorage>> +   withdrawalClaimDelay: uint256 <<VaultStorage>> +   weth: address <<OETHVaultCore>> +   wethAssetIndex: uint256 <<OETHVaultCore>> + +Private: +    abs(x: int256): uint256 <<VaultCore>> +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _mint(_asset: address, _amount: uint256, _minimumOusdAmount: uint256) <<OETHVaultCore>> +    _redeem(_amount: uint256, _minimumUnitAmount: uint256) <<OETHBaseVaultCore>> +    _postRedeem(_amount: uint256) <<VaultCore>> +    _allocate() <<OETHVaultCore>> +    _rebase(): uint256 <<whenNotRebasePaused>> <<VaultCore>> +    _totalValue(): (value: uint256) <<OETHVaultCore>> +    _totalValueInVault(): (value: uint256) <<OETHVaultCore>> +    _totalValueInStrategies(): (value: uint256) <<VaultCore>> +    _totalValueInStrategy(_strategyAddr: address): (value: uint256) <<VaultCore>> +    _checkBalance(_asset: address): (balance: uint256) <<OETHVaultCore>> +    _calculateRedeemOutputs(_amount: uint256): (outputs: uint256[]) <<OETHVaultCore>> +    _toUnits(_raw: uint256, _asset: address): uint256 <<VaultCore>> +    _toUnitPrice(_asset: address, isMint: bool): (price: uint256) <<VaultCore>> +    _getDecimals(_asset: address): (decimals: uint256) <<VaultCore>> +    _claimWithdrawal(requestId: uint256): (amount: uint256) <<OETHVaultCore>> +    _addWithdrawalQueueLiquidity(): (addedClaimable: uint256) <<OETHVaultCore>> +    _wethAvailable(): (wethAvailable: uint256) <<OETHVaultCore>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    setAdminImpl(newImpl: address) <<onlyGovernor>> <<VaultStorage>> +    initialize(_priceProvider: address, _oToken: address) <<onlyGovernor, initializer>> <<VaultInitializer>> +    mint(_asset: address, _amount: uint256, _minimumOusdAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> +    mintForStrategy(amount: uint256) <<whenNotCapitalPaused>> <<OETHVaultCore>> +    redeem(_amount: uint256, _minimumUnitAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> +    burnForStrategy(amount: uint256) <<whenNotCapitalPaused>> <<OETHVaultCore>> +    redeemAll(_minimumUnitAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> +    allocate() <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> +    rebase() <<nonReentrant>> <<VaultCore>> +    totalValue(): (value: uint256) <<VaultCore>> +    checkBalance(_asset: address): uint256 <<VaultCore>> +    calculateRedeemOutputs(_amount: uint256): uint256[] <<VaultCore>> +    priceUnitMint(asset: address): (price: uint256) <<VaultCore>> +    priceUnitRedeem(asset: address): (price: uint256) <<VaultCore>> +    getAllAssets(): address[] <<VaultCore>> +    getStrategyCount(): uint256 <<VaultCore>> +    getAllStrategies(): address[] <<VaultCore>> +    isSupportedAsset(_asset: address): bool <<VaultCore>> +    null() <<VaultCore>> +    cacheWETHAssetIndex() <<onlyGovernor>> <<OETHVaultCore>> +    requestWithdrawal(_amount: uint256): (requestId: uint256, queued: uint256) <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> +    claimWithdrawal(_requestId: uint256): (amount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> +    claimWithdrawals(_requestIds: uint256[]): (amounts: uint256[], totalAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> +    addWithdrawalQueueLiquidity() <<OETHVaultCore>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> AssetSupported(_asset: address) <<VaultStorage>> +    <<event>> AssetRemoved(_asset: address) <<VaultStorage>> +    <<event>> AssetDefaultStrategyUpdated(_asset: address, _strategy: address) <<VaultStorage>> +    <<event>> AssetAllocated(_asset: address, _strategy: address, _amount: uint256) <<VaultStorage>> +    <<event>> StrategyApproved(_addr: address) <<VaultStorage>> +    <<event>> StrategyRemoved(_addr: address) <<VaultStorage>> +    <<event>> Mint(_addr: address, _value: uint256) <<VaultStorage>> +    <<event>> Redeem(_addr: address, _value: uint256) <<VaultStorage>> +    <<event>> CapitalPaused() <<VaultStorage>> +    <<event>> CapitalUnpaused() <<VaultStorage>> +    <<event>> RebasePaused() <<VaultStorage>> +    <<event>> RebaseUnpaused() <<VaultStorage>> +    <<event>> VaultBufferUpdated(_vaultBuffer: uint256) <<VaultStorage>> +    <<event>> OusdMetaStrategyUpdated(_ousdMetaStrategy: address) <<VaultStorage>> +    <<event>> RedeemFeeUpdated(_redeemFeeBps: uint256) <<VaultStorage>> +    <<event>> PriceProviderUpdated(_priceProvider: address) <<VaultStorage>> +    <<event>> AllocateThresholdUpdated(_threshold: uint256) <<VaultStorage>> +    <<event>> RebaseThresholdUpdated(_threshold: uint256) <<VaultStorage>> +    <<event>> StrategistUpdated(_address: address) <<VaultStorage>> +    <<event>> MaxSupplyDiffChanged(maxSupplyDiff: uint256) <<VaultStorage>> +    <<event>> YieldDistribution(_to: address, _yield: uint256, _fee: uint256) <<VaultStorage>> +    <<event>> TrusteeFeeBpsChanged(_basis: uint256) <<VaultStorage>> +    <<event>> TrusteeAddressChanged(_address: address) <<VaultStorage>> +    <<event>> NetOusdMintForStrategyThresholdChanged(_threshold: uint256) <<VaultStorage>> +    <<event>> SwapperChanged(_address: address) <<VaultStorage>> +    <<event>> SwapAllowedUndervalueChanged(_basis: uint256) <<VaultStorage>> +    <<event>> SwapSlippageChanged(_asset: address, _basis: uint256) <<VaultStorage>> +    <<event>> Swapped(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _toAssetAmount: uint256) <<VaultStorage>> +    <<event>> StrategyAddedToMintWhitelist(strategy: address) <<VaultStorage>> +    <<event>> StrategyRemovedFromMintWhitelist(strategy: address) <<VaultStorage>> +    <<event>> DripperChanged(_dripper: address) <<VaultStorage>> +    <<event>> WithdrawalRequested(_withdrawer: address, _requestId: uint256, _amount: uint256, _queued: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimed(_withdrawer: address, _requestId: uint256, _amount: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimable(_claimable: uint256, _newClaimable: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimDelayUpdated(_newDelay: uint256) <<VaultStorage>> +    <<modifier>> initializer() <<Initializable>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    <<modifier>> whenNotRebasePaused() <<VaultCore>> +    <<modifier>> whenNotCapitalPaused() <<VaultCore>> +    <<modifier>> onlyOusdMetaStrategy() <<VaultCore>> +    constructor() <<Governable>> +    governor(): address <<Governable>> +    isGovernor(): bool <<Governable>> +    getAssetCount(): uint256 <<VaultCore>> +    getAssetConfig(_asset: address): (config: Asset) <<VaultCore>> +    constructor(_weth: address) <<OETHBaseVaultCore>> + + + diff --git a/contracts/docs/OETHBaseVaultStorage.svg b/contracts/docs/OETHBaseVaultStorage.svg new file mode 100644 index 0000000000..f768dd006b --- /dev/null +++ b/contracts/docs/OETHBaseVaultStorage.svg @@ -0,0 +1,382 @@ + + + + + + +StorageDiagram + + + +9 + +OETHBaseVaultCore <<Contract>> + +slot + +0 + +1-50 + +51 + +52 + +53 + +54 + +55 + +56 + +57 + +58 + +59 + +60 + +61 + +62 + +63 + +64 + +65 + +66 + +67 + +68 + +69 + +70 + +71 + +72 + +73 + +74 + +75-76 + +77 + +78 + +79-122 + +123 + +124-173 + +type: <inherited contract>.variable (bytes) + +unallocated (30) + +bool: Initializable.initializing (1) + +bool: Initializable.initialized (1) + +uint256[50]: Initializable.______gap (1600) + +mapping(address=>Asset): VaultStorage.assets (32) + +address[]: VaultStorage.allAssets (32) + +mapping(address=>Strategy): VaultStorage.strategies (32) + +address[]: VaultStorage.allStrategies (32) + +unallocated (10) + +bool: VaultStorage.capitalPaused (1) + +bool: VaultStorage.rebasePaused (1) + +address: VaultStorage.priceProvider (20) + +uint256: VaultStorage.redeemFeeBps (32) + +uint256: VaultStorage.vaultBuffer (32) + +uint256: VaultStorage.autoAllocateThreshold (32) + +uint256: VaultStorage.rebaseThreshold (32) + +unallocated (12) + +OUSD: VaultStorage.oUSD (20) + +unallocated (12) + +address: VaultStorage._deprecated_rebaseHooksAddr (20) + +unallocated (12) + +address: VaultStorage._deprecated_uniswapAddr (20) + +unallocated (12) + +address: VaultStorage.strategistAddr (20) + +mapping(address=>address): VaultStorage.assetDefaultStrategies (32) + +uint256: VaultStorage.maxSupplyDiff (32) + +unallocated (12) + +address: VaultStorage.trusteeAddress (20) + +uint256: VaultStorage.trusteeFeeBps (32) + +address[]: VaultStorage._deprecated_swapTokens (32) + +unallocated (12) + +address: VaultStorage.ousdMetaStrategy (20) + +int256: VaultStorage.netOusdMintedForStrategy (32) + +uint256: VaultStorage.netOusdMintForStrategyThreshold (32) + +SwapConfig: VaultStorage.swapConfig (32) + +mapping(address=>bool): VaultStorage.isMintWhitelistedStrategy (32) + +unallocated (12) + +address: VaultStorage.dripper (20) + +WithdrawalQueueMetadata: VaultStorage.withdrawalQueueMetadata (64) + +mapping(uint256=>WithdrawalRequest): VaultStorage.withdrawalRequests (32) + +uint256: VaultStorage.withdrawalClaimDelay (32) + +uint256[44]: VaultStorage.__gap (1408) + +uint256: OETHVaultCore.wethAssetIndex (32) + +uint256[50]: OETHVaultCore.__gap (1600) + + + +1 + +Asset <<Struct>> + +offset + +0 + +type: variable (bytes) + +unallocated (27) + +uint16: allowedOracleSlippageBps (2) + +uint8: decimals (1) + +UnitConversion: unitConversion (1) + +bool: isSupported (1) + + + +9:8->1 + + + + + +2 + +address[]: allAssets <<Array>> +0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +9:10->2 + + + + + +3 + +Strategy <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +unallocated (31) + +bool: isSupported (1) + +uint256: _deprecated (32) + + + +9:13->3 + + + + + +4 + +address[]: allStrategies <<Array>> +0x4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b8 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +9:15->4 + + + + + +5 + +address[]: _deprecated_swapTokens <<Array>> +0x9b22d3d61959b4d3528b1d8ba932c96fbe302b36a1aad1d95cab54f9e0a135ea + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +9:32->5 + + + + + +6 + +SwapConfig <<Struct>> + +slot + +72 + +type: variable (bytes) + +unallocated (10) + +uint16: allowedUndervalueBps (2) + +address: swapper (20) + + + +9:38->6 + + + + + +7 + +WithdrawalQueueMetadata <<Struct>> + +slot + +75 + +76 + +type: variable (bytes) + +uint128: claimable (16) + +uint128: queued (16) + +uint128: nextWithdrawalIndex (16) + +uint128: claimed (16) + + + +9:45->7 + + + + + +8 + +WithdrawalRequest <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +unallocated (6) + +uint40: timestamp (5) + +bool: claimed (1) + +address: withdrawer (20) + +uint128: queued (16) + +uint128: amount (16) + + + +9:51->8 + + + + + diff --git a/contracts/docs/OETHDripperHierarchy.svg b/contracts/docs/OETHDripperHierarchy.svg deleted file mode 100644 index 9641ea80ca..0000000000 --- a/contracts/docs/OETHDripperHierarchy.svg +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - -UmlClassDiagram - - - -20 - -Governable -../contracts/governance/Governable.sol - - - -30 - -Dripper -../contracts/harvest/Dripper.sol - - - -30->20 - - - - - -61 - -<<Interface>> -IVault -../contracts/interfaces/IVault.sol - - - -30->61 - - - - - -710 - -<<Interface>> -IERC20 -../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol - - - -30->710 - - - - - -33 - -OETHDripper -../contracts/harvest/OETHDripper.sol - - - -33->30 - - - - - -239 - -VaultStorage -../contracts/vault/VaultStorage.sol - - - -61->239 - - - - - -215 - -OUSD -../contracts/token/OUSD.sol - - - -215->20 - - - - - -224 - -<<Abstract>> -Initializable -../contracts/utils/Initializable.sol - - - -215->224 - - - - - -227 - -<<Abstract>> -InitializableERC20Detailed -../contracts/utils/InitializableERC20Detailed.sol - - - -215->227 - - - - - -227->710 - - - - - -239->20 - - - - - -239->215 - - - - - -239->224 - - - - - diff --git a/contracts/docs/OETHFixedRateDripperHierarchy.svg b/contracts/docs/OETHFixedRateDripperHierarchy.svg new file mode 100644 index 0000000000..10197dd15b --- /dev/null +++ b/contracts/docs/OETHFixedRateDripperHierarchy.svg @@ -0,0 +1,59 @@ + + + + + + +UmlClassDiagram + + + +21 + +Governable +../contracts/governance/Governable.sol + + + +31 + +Dripper +../contracts/harvest/Dripper.sol + + + +31->21 + + + + + +33 + +FixedRateDripper +../contracts/harvest/FixedRateDripper.sol + + + +33->31 + + + + + +37 + +OETHFixedRateDripper +../contracts/harvest/OETHFixedRateDripper.sol + + + +37->33 + + + + + diff --git a/contracts/docs/OETHDripperSquashed.svg b/contracts/docs/OETHFixedRateDripperSquashed.svg similarity index 72% rename from contracts/docs/OETHDripperSquashed.svg rename to contracts/docs/OETHFixedRateDripperSquashed.svg index 7c76032208..f27d2a587b 100644 --- a/contracts/docs/OETHDripperSquashed.svg +++ b/contracts/docs/OETHFixedRateDripperSquashed.svg @@ -4,55 +4,59 @@ - - + + UmlClassDiagram - - + + -33 - -OETHDripper -../contracts/harvest/OETHDripper.sol - -Private: -   governorPosition: bytes32 <<Governable>> -   pendingGovernorPosition: bytes32 <<Governable>> -   reentryStatusPosition: bytes32 <<Governable>> -Public: -   _NOT_ENTERED: uint256 <<Governable>> -   _ENTERED: uint256 <<Governable>> -   vault: address <<Dripper>> -   token: address <<Dripper>> -   dripDuration: uint256 <<Dripper>> -   drip: Drip <<Dripper>> - -Internal: -    _governor(): (governorOut: address) <<Governable>> -    _pendingGovernor(): (pendingGovernor: address) <<Governable>> -    _setGovernor(newGovernor: address) <<Governable>> -    _setPendingGovernor(newGovernor: address) <<Governable>> -    _changeGovernor(_newGovernor: address) <<Governable>> -    _availableFunds(_balance: uint256, _drip: Drip): uint256 <<Dripper>> -    _collect() <<Dripper>> -External: -    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> -    claimGovernance() <<Governable>> -    availableFunds(): uint256 <<Dripper>> -    collect() <<Dripper>> -    collectAndRebase() <<Dripper>> -    setDripDuration(_durationSeconds: uint256) <<onlyGovernor>> <<Dripper>> -    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<Dripper>> -Public: -    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> -    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> -    <<modifier>> onlyGovernor() <<Governable>> -    <<modifier>> nonReentrant() <<Governable>> +37 + +OETHFixedRateDripper +../contracts/harvest/OETHFixedRateDripper.sol + +Private: +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   vault: address <<Dripper>> +   token: address <<Dripper>> +   dripDuration: uint256 <<Dripper>> +   drip: Drip <<Dripper>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _availableFunds(_balance: uint256, _drip: Drip): uint256 <<Dripper>> +    _collect() <<FixedRateDripper>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    availableFunds(): uint256 <<Dripper>> +    collect() <<Dripper>> +    collectAndRebase() <<Dripper>> +    setDripDuration(uint256) <<FixedRateDripper>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<Dripper>> +    transferAllToken(_asset: address, _receiver: address) <<onlyGovernor>> <<Dripper>> +    setDripRate(_perSecond: uint192) <<onlyGovernorOrStrategist>> <<FixedRateDripper>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> DripRateUpdated(oldDripRate: uint192, newDripRate: uint192) <<FixedRateDripper>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    <<modifier>> onlyGovernorOrStrategist() <<FixedRateDripper>>    constructor() <<Governable>>    governor(): address <<Governable>>    isGovernor(): bool <<Governable>> -    constructor(_vault: address, _token: address) <<OETHDripper>> +    constructor(_vault: address, _token: address) <<OETHFixedRateDripper>> diff --git a/contracts/docs/OETHDripperStorage.svg b/contracts/docs/OETHFixedRateDripperStorage.svg similarity index 74% rename from contracts/docs/OETHDripperStorage.svg rename to contracts/docs/OETHFixedRateDripperStorage.svg index e2da1f7190..867ab42847 100644 --- a/contracts/docs/OETHDripperStorage.svg +++ b/contracts/docs/OETHFixedRateDripperStorage.svg @@ -4,16 +4,16 @@ - + StorageDiagram - + 2 -OETHDripper <<Contract>> +OETHFixedRateDripper <<Contract>> slot @@ -30,24 +30,24 @@ 1 - -Drip <<Struct>> - + +Drip <<Struct>> + slot 1 -type: variable (bytes) - -uint192: perSecond (24) - -uint64: lastCollect (8) +type: variable (bytes) + +uint192: perSecond (24) + +uint64: lastCollect (8) 2:4->1 - - + + diff --git a/contracts/docs/OETHHarvesterHierarchy.svg b/contracts/docs/OETHHarvesterHierarchy.svg deleted file mode 100644 index bdc6c88e80..0000000000 --- a/contracts/docs/OETHHarvesterHierarchy.svg +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - -UmlClassDiagram - - - -20 - -Governable -../contracts/governance/Governable.sol - - - -26 - -<<Abstract>> -AbstractHarvester -../contracts/harvest/AbstractHarvester.sol - - - -26->20 - - - - - -51 - -<<Interface>> -IOracle -../contracts/interfaces/IOracle.sol - - - -26->51 - - - - - -57 - -<<Interface>> -IStrategy -../contracts/interfaces/IStrategy.sol - - - -26->57 - - - - - -61 - -<<Interface>> -IVault -../contracts/interfaces/IVault.sol - - - -26->61 - - - - - -249 - -<<Interface>> -IBalancerVault -../contracts/interfaces/balancer/IBalancerVault.sol - - - -26->249 - - - - - -280 - -<<Interface>> -IUniswapV2Router -../contracts/interfaces/uniswap/IUniswapV2Router02.sol - - - -26->280 - - - - - -281 - -<<Interface>> -IUniswapV3Router -../contracts/interfaces/uniswap/IUniswapV3Router.sol - - - -26->281 - - - - - -199 - -<<Interface>> -ICurvePool -../contracts/strategies/ICurvePool.sol - - - -26->199 - - - - - -710 - -<<Interface>> -IERC20 -../node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol - - - -26->710 - - - - - -34 - -OETHHarvester -../contracts/harvest/OETHHarvester.sol - - - -34->26 - - - - - -239 - -VaultStorage -../contracts/vault/VaultStorage.sol - - - -61->239 - - - - - -215 - -OUSD -../contracts/token/OUSD.sol - - - -215->20 - - - - - -224 - -<<Abstract>> -Initializable -../contracts/utils/Initializable.sol - - - -215->224 - - - - - -227 - -<<Abstract>> -InitializableERC20Detailed -../contracts/utils/InitializableERC20Detailed.sol - - - -215->227 - - - - - -227->710 - - - - - -239->20 - - - - - -239->215 - - - - - -239->224 - - - - - diff --git a/contracts/docs/OETHHarvesterSimple.svg b/contracts/docs/OETHHarvesterSimple.svg new file mode 100644 index 0000000000..df4be2d1ff --- /dev/null +++ b/contracts/docs/OETHHarvesterSimple.svg @@ -0,0 +1,74 @@ + + + + + + +StorageDiagram + + + +2 + +OETHHarvesterSimple <<Contract>> +0x399B69Bf06CCec7A53Befea14771059d39a3617a + +slot + +0 + +1-50 + +51 + +type: <inherited contract>.variable (bytes) + +unallocated (12) + +address: Strategizable.strategistAddr (20) + +uint256[50]: Strategizable.__gap (1600) + +mapping(address=>bool): supportedStrategies (32) + + + +1 + +uint256[50]: __gap <<Array>> + +slot + +1 + +2 + +3-48 + +49 + +50 + +type: variable (bytes) + +uint256 (32) + +uint256 (32) + +---- (1472) + +uint256 (32) + +uint256 (32) + + + +2:7->1 + + + + + diff --git a/contracts/docs/OETHHarvesterSimpleHierarchy.svg b/contracts/docs/OETHHarvesterSimpleHierarchy.svg new file mode 100644 index 0000000000..7db0977ac7 --- /dev/null +++ b/contracts/docs/OETHHarvesterSimpleHierarchy.svg @@ -0,0 +1,60 @@ + + + + + + +UmlClassDiagram + + + +21 + +Governable +../contracts/governance/Governable.sol + + + +26 + +Strategizable +../contracts/governance/Strategizable.sol + + + +26->21 + + + + + +39 + +OETHHarvesterSimple +../contracts/harvest/OETHHarvesterSimple.sol + + + +39->26 + + + + + +279 + +<<Abstract>> +Initializable +../contracts/utils/Initializable.sol + + + +39->279 + + + + + diff --git a/contracts/docs/OETHHarvesterSimpleSquashed.svg b/contracts/docs/OETHHarvesterSimpleSquashed.svg new file mode 100644 index 0000000000..c84fabad94 --- /dev/null +++ b/contracts/docs/OETHHarvesterSimpleSquashed.svg @@ -0,0 +1,72 @@ + + + + + + +UmlClassDiagram + + + +39 + +OETHHarvesterSimple +../contracts/harvest/OETHHarvesterSimple.sol + +Private: +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   __gap: uint256[50] <<Strategizable>> +   ___gap: uint256[48] <<OETHHarvesterSimple>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   strategistAddr: address <<Strategizable>> +   wrappedNativeToken: address <<OETHHarvesterSimple>> +   dripper: address <<OETHHarvesterSimple>> +   supportedStrategies: mapping(address=>bool) <<OETHHarvesterSimple>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _setStrategistAddr(_address: address) <<Strategizable>> +    _harvestAndTransfer(_strategy: address) <<OETHHarvesterSimple>> +    _setDripper(_dripper: address) <<OETHHarvesterSimple>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    setStrategistAddr(_address: address) <<onlyGovernor>> <<Strategizable>> +    initialize(_governor: address, _strategist: address, _dripper: address) <<initializer>> <<OETHHarvesterSimple>> +    harvestAndTransfer(_strategy: address) <<OETHHarvesterSimple>> +    harvestAndTransfer(_strategies: address[]) <<OETHHarvesterSimple>> +    setSupportedStrategy(_strategy: address, _isSupported: bool) <<onlyGovernorOrStrategist>> <<OETHHarvesterSimple>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernorOrStrategist>> <<OETHHarvesterSimple>> +    setDripper(_dripper: address) <<onlyGovernor>> <<OETHHarvesterSimple>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> StrategistUpdated(_address: address) <<Strategizable>> +    <<event>> Harvested(strategy: address, token: address, amount: uint256, receiver: address) <<OETHHarvesterSimple>> +    <<event>> SupportedStrategyUpdated(strategy: address, status: bool) <<OETHHarvesterSimple>> +    <<event>> DripperUpdated(dripper: address) <<OETHHarvesterSimple>> +    <<modifier>> initializer() <<Initializable>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    <<modifier>> onlyGovernorOrStrategist() <<Strategizable>> +    constructor() <<Governable>> +    governor(): address <<Governable>> +    isGovernor(): bool <<Governable>> +    constructor(_wrappedNativeToken: address) <<OETHHarvesterSimple>> + + + diff --git a/contracts/docs/OETHHarvesterSimpleStorage.svg b/contracts/docs/OETHHarvesterSimpleStorage.svg new file mode 100644 index 0000000000..aef109c1e7 --- /dev/null +++ b/contracts/docs/OETHHarvesterSimpleStorage.svg @@ -0,0 +1,59 @@ + + + + + + +StorageDiagram + + + +1 + +OETHHarvesterSimple <<Contract>> + +slot + +0 + +1-50 + +51 + +52-101 + +102 + +103 + +104-151 + +type: <inherited contract>.variable (bytes) + +unallocated (30) + +bool: Initializable.initializing (1) + +bool: Initializable.initialized (1) + +uint256[50]: Initializable.______gap (1600) + +unallocated (12) + +address: Strategizable.strategistAddr (20) + +uint256[50]: Strategizable.__gap (1600) + +unallocated (12) + +address: dripper (20) + +mapping(address=>bool): supportedStrategies (32) + +uint256[48]: ___gap (1536) + + + diff --git a/contracts/docs/OETHHarvesterSquashed.svg b/contracts/docs/OETHHarvesterSquashed.svg deleted file mode 100644 index bbe36e0649..0000000000 --- a/contracts/docs/OETHHarvesterSquashed.svg +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - -UmlClassDiagram - - - -34 - -OETHHarvester -../contracts/harvest/OETHHarvester.sol - -Private: -   governorPosition: bytes32 <<Governable>> -   pendingGovernorPosition: bytes32 <<Governable>> -   reentryStatusPosition: bytes32 <<Governable>> -Public: -   _NOT_ENTERED: uint256 <<Governable>> -   _ENTERED: uint256 <<Governable>> -   rewardTokenConfigs: mapping(address=>RewardTokenConfig) <<AbstractHarvester>> -   supportedStrategies: mapping(address=>bool) <<AbstractHarvester>> -   vaultAddress: address <<AbstractHarvester>> -   rewardProceedsAddress: address <<AbstractHarvester>> -   baseTokenAddress: address <<AbstractHarvester>> -   baseTokenDecimals: uint256 <<AbstractHarvester>> -   uniswapV2Path: mapping(address=>address[]) <<AbstractHarvester>> -   uniswapV3Path: mapping(address=>bytes) <<AbstractHarvester>> -   balancerPoolId: mapping(address=>bytes32) <<AbstractHarvester>> -   curvePoolIndices: mapping(address=>CurvePoolIndices) <<AbstractHarvester>> - -Internal: -    _governor(): (governorOut: address) <<Governable>> -    _pendingGovernor(): (pendingGovernor: address) <<Governable>> -    _setGovernor(newGovernor: address) <<Governable>> -    _setPendingGovernor(newGovernor: address) <<Governable>> -    _changeGovernor(_newGovernor: address) <<Governable>> -    _decodeUniswapV2Path(data: bytes, token: address): (path: address[]) <<AbstractHarvester>> -    _decodeUniswapV3Path(data: bytes, token: address): (path: bytes) <<AbstractHarvester>> -    _decodeBalancerPoolId(data: bytes, balancerVault: address, token: address): (poolId: bytes32) <<AbstractHarvester>> -    _decodeCurvePoolIndices(data: bytes, poolAddress: address, token: address): (indices: CurvePoolIndices) <<AbstractHarvester>> -    _harvestAndSwap(_strategyAddr: address, _rewardTo: address) <<AbstractHarvester>> -    _harvest(_strategyAddr: address) <<AbstractHarvester>> -    _swap(_swapToken: address, _rewardTo: address, _priceProvider: IOracle) <<AbstractHarvester>> -    _doSwap(swapPlatform: SwapPlatform, routerAddress: address, rewardTokenAddress: address, amountIn: uint256, minAmountOut: uint256): (amountOut: uint256) <<AbstractHarvester>> -    _swapWithUniswapV2(routerAddress: address, swapToken: address, amountIn: uint256, minAmountOut: uint256): (amountOut: uint256) <<AbstractHarvester>> -    _swapWithUniswapV3(routerAddress: address, swapToken: address, amountIn: uint256, minAmountOut: uint256): (amountOut: uint256) <<AbstractHarvester>> -    _swapWithBalancer(balancerVaultAddress: address, swapToken: address, amountIn: uint256, minAmountOut: uint256): (amountOut: uint256) <<AbstractHarvester>> -    _swapWithCurve(poolAddress: address, swapToken: address, amountIn: uint256, minAmountOut: uint256): (amountOut: uint256) <<AbstractHarvester>> -External: -    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> -    claimGovernance() <<Governable>> -    setRewardProceedsAddress(_rewardProceedsAddress: address) <<onlyGovernor>> <<AbstractHarvester>> -    setRewardTokenConfig(_tokenAddress: address, tokenConfig: RewardTokenConfig, swapData: bytes) <<onlyGovernor>> <<AbstractHarvester>> -    setSupportedStrategy(_strategyAddress: address, _isSupported: bool) <<onlyGovernor>> <<AbstractHarvester>> -    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<AbstractHarvester>> -    harvestAndSwap(_strategyAddr: address) <<nonReentrant>> <<AbstractHarvester>> -    harvestAndSwap(_strategyAddr: address, _rewardTo: address) <<nonReentrant>> <<AbstractHarvester>> -Public: -    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> -    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> -    <<event>> SupportedStrategyUpdate(strategyAddress: address, isSupported: bool) <<AbstractHarvester>> -    <<event>> RewardTokenConfigUpdated(tokenAddress: address, allowedSlippageBps: uint16, harvestRewardBps: uint16, swapPlatform: SwapPlatform, swapPlatformAddr: address, swapData: bytes, liquidationLimit: uint256, doSwapRewardToken: bool) <<AbstractHarvester>> -    <<event>> RewardTokenSwapped(rewardToken: address, swappedInto: address, swapPlatform: SwapPlatform, amountIn: uint256, amountOut: uint256) <<AbstractHarvester>> -    <<event>> RewardProceedsTransferred(token: address, farmer: address, protcolYield: uint256, farmerFee: uint256) <<AbstractHarvester>> -    <<event>> RewardProceedsAddressChanged(newProceedsAddress: address) <<AbstractHarvester>> -    <<modifier>> onlyGovernor() <<Governable>> -    <<modifier>> nonReentrant() <<Governable>> -    constructor() <<Governable>> -    governor(): address <<Governable>> -    isGovernor(): bool <<Governable>> -    constructor(_vault: address, _wethAddress: address) <<OETHHarvester>> - - - diff --git a/contracts/docs/OETHHarvesterStorage.svg b/contracts/docs/OETHHarvesterStorage.svg deleted file mode 100644 index 44e286ef68..0000000000 --- a/contracts/docs/OETHHarvesterStorage.svg +++ /dev/null @@ -1,109 +0,0 @@ - - - - - - -StorageDiagram - - - -3 - -OETHHarvester <<Contract>> - -slot - -0 - -1 - -2 - -3 - -4 - -5 - -6 - -type: <inherited contract>.variable (bytes) - -mapping(address=>RewardTokenConfig): AbstractHarvester.rewardTokenConfigs (32) - -mapping(address=>bool): AbstractHarvester.supportedStrategies (32) - -unallocated (12) - -address: AbstractHarvester.rewardProceedsAddress (20) - -mapping(address=>address[]): AbstractHarvester.uniswapV2Path (32) - -mapping(address=>bytes): AbstractHarvester.uniswapV3Path (32) - -mapping(address=>bytes32): AbstractHarvester.balancerPoolId (32) - -mapping(address=>CurvePoolIndices): AbstractHarvester.curvePoolIndices (32) - - - -1 - -RewardTokenConfig <<Struct>> - -offset - -0 - -1 - -type: variable (bytes) - -unallocated (6) - -SwapPlatform: swapPlatform (1) - -bool: doSwapRewardToken (1) - -address: swapPlatformAddr (20) - -uint16: harvestRewardBps (2) - -uint16: allowedSlippageBps (2) - -uint256: liquidationLimit (32) - - - -3:7->1 - - - - - -2 - -CurvePoolIndices <<Struct>> - -offset - -0 - -type: variable (bytes) - -uint128: baseTokenIndex (16) - -uint128: rewardTokenIndex (16) - - - -3:15->2 - - - - - diff --git a/contracts/docs/OETHVaultAdminSquashed.svg b/contracts/docs/OETHVaultAdminSquashed.svg index d5af64e4d8..42ba2da7ee 100644 --- a/contracts/docs/OETHVaultAdminSquashed.svg +++ b/contracts/docs/OETHVaultAdminSquashed.svg @@ -4,151 +4,156 @@ - - + + UmlClassDiagram - - + + -233 - -OETHVaultAdmin -../contracts/vault/OETHVaultAdmin.sol - -Private: -   initialized: bool <<Initializable>> -   initializing: bool <<Initializable>> -   ______gap: uint256[50] <<Initializable>> -   governorPosition: bytes32 <<Governable>> -   pendingGovernorPosition: bytes32 <<Governable>> -   reentryStatusPosition: bytes32 <<Governable>> -   _deprecated_rebaseHooksAddr: address <<VaultStorage>> -   _deprecated_uniswapAddr: address <<VaultStorage>> -   _deprecated_swapTokens: address[] <<VaultStorage>> -   __gap: uint256[45] <<VaultStorage>> -Internal: -   assets: mapping(address=>Asset) <<VaultStorage>> -   allAssets: address[] <<VaultStorage>> -   strategies: mapping(address=>Strategy) <<VaultStorage>> -   allStrategies: address[] <<VaultStorage>> -   oUSD: OUSD <<VaultStorage>> -   swapConfig: SwapConfig <<VaultStorage>> -Public: -   _NOT_ENTERED: uint256 <<Governable>> -   _ENTERED: uint256 <<Governable>> -   priceProvider: address <<VaultStorage>> -   rebasePaused: bool <<VaultStorage>> -   capitalPaused: bool <<VaultStorage>> -   redeemFeeBps: uint256 <<VaultStorage>> -   vaultBuffer: uint256 <<VaultStorage>> -   autoAllocateThreshold: uint256 <<VaultStorage>> -   rebaseThreshold: uint256 <<VaultStorage>> -   adminImplPosition: bytes32 <<VaultStorage>> -   strategistAddr: address <<VaultStorage>> -   assetDefaultStrategies: mapping(address=>address) <<VaultStorage>> -   maxSupplyDiff: uint256 <<VaultStorage>> -   trusteeAddress: address <<VaultStorage>> -   trusteeFeeBps: uint256 <<VaultStorage>> -   MINT_MINIMUM_UNIT_PRICE: uint256 <<VaultStorage>> -   ousdMetaStrategy: address <<VaultStorage>> -   netOusdMintedForStrategy: int256 <<VaultStorage>> -   netOusdMintForStrategyThreshold: uint256 <<VaultStorage>> -   MIN_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> -   MAX_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> -   isMintWhitelistedStrategy: mapping(address=>bool) <<VaultStorage>> -   dripper: address <<VaultStorage>> -   withdrawalQueueMetadata: WithdrawalQueueMetadata <<VaultStorage>> -   withdrawalRequests: mapping(uint256=>WithdrawalRequest) <<VaultStorage>> -   weth: address <<OETHVaultAdmin>> - -Internal: -    _governor(): (governorOut: address) <<Governable>> -    _pendingGovernor(): (pendingGovernor: address) <<Governable>> -    _setGovernor(newGovernor: address) <<Governable>> -    _setPendingGovernor(newGovernor: address) <<Governable>> -    _changeGovernor(_newGovernor: address) <<Governable>> -    _swapCollateral(address, address, uint256, uint256, bytes): uint256 <<OETHVaultAdmin>> -    _depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> -    _withdrawFromStrategy(_recipient: address, _strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> -    _withdrawAllFromStrategy(_strategyAddr: address) <<OETHVaultAdmin>> -    _withdrawAllFromStrategies() <<OETHVaultAdmin>> -    _cacheDecimals(token: address) <<VaultAdmin>> -    _wethAvailable(): (wethAvailable: uint256) <<OETHVaultAdmin>> -External: -    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> -    claimGovernance() <<Governable>> -    setAdminImpl(newImpl: address) <<onlyGovernor>> <<VaultStorage>> -    setPriceProvider(_priceProvider: address) <<onlyGovernor>> <<VaultAdmin>> -    setRedeemFeeBps(_redeemFeeBps: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setVaultBuffer(_vaultBuffer: uint256) <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    setAutoAllocateThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setRebaseThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setStrategistAddr(_address: address) <<onlyGovernor>> <<VaultAdmin>> -    setAssetDefaultStrategy(_asset: address, _strategy: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    setNetOusdMintForStrategyThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setDripper(_dripper: address) <<onlyGovernor>> <<VaultAdmin>> -    swapCollateral(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _minToAssetAmount: uint256, _data: bytes): (toAssetAmount: uint256) <<nonReentrant, onlyGovernorOrStrategist>> <<VaultAdmin>> -    setSwapper(_swapperAddr: address) <<onlyGovernor>> <<VaultAdmin>> -    swapper(): (swapper_: address) <<VaultAdmin>> -    setSwapAllowedUndervalue(_basis: uint16) <<onlyGovernor>> <<VaultAdmin>> -    allowedSwapUndervalue(): (value: uint256) <<VaultAdmin>> -    setOracleSlippage(_asset: address, _allowedOracleSlippageBps: uint16) <<onlyGovernor>> <<VaultAdmin>> -    supportAsset(_asset: address, _unitConversion: uint8) <<onlyGovernor>> <<VaultAdmin>> -    removeAsset(_asset: address) <<onlyGovernor>> <<VaultAdmin>> -    cacheDecimals(_asset: address) <<onlyGovernor>> <<VaultAdmin>> -    approveStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> -    removeStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> -    depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> -    withdrawFromStrategy(_strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> -    setMaxSupplyDiff(_maxSupplyDiff: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setTrusteeAddress(_address: address) <<onlyGovernor>> <<VaultAdmin>> -    setTrusteeFeeBps(_basis: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setOusdMetaStrategy(_ousdMetaStrategy: address) <<onlyGovernor>> <<VaultAdmin>> -    pauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    unpauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    pauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    unpauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<VaultAdmin>> -    withdrawAllFromStrategy(_strategyAddr: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    withdrawAllFromStrategies() <<onlyGovernorOrStrategist>> <<VaultAdmin>> -Public: -    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> -    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> -    <<event>> AssetSupported(_asset: address) <<VaultStorage>> -    <<event>> AssetRemoved(_asset: address) <<VaultStorage>> -    <<event>> AssetDefaultStrategyUpdated(_asset: address, _strategy: address) <<VaultStorage>> -    <<event>> AssetAllocated(_asset: address, _strategy: address, _amount: uint256) <<VaultStorage>> -    <<event>> StrategyApproved(_addr: address) <<VaultStorage>> -    <<event>> StrategyRemoved(_addr: address) <<VaultStorage>> -    <<event>> Mint(_addr: address, _value: uint256) <<VaultStorage>> -    <<event>> Redeem(_addr: address, _value: uint256) <<VaultStorage>> -    <<event>> CapitalPaused() <<VaultStorage>> -    <<event>> CapitalUnpaused() <<VaultStorage>> -    <<event>> RebasePaused() <<VaultStorage>> -    <<event>> RebaseUnpaused() <<VaultStorage>> -    <<event>> VaultBufferUpdated(_vaultBuffer: uint256) <<VaultStorage>> -    <<event>> OusdMetaStrategyUpdated(_ousdMetaStrategy: address) <<VaultStorage>> -    <<event>> RedeemFeeUpdated(_redeemFeeBps: uint256) <<VaultStorage>> -    <<event>> PriceProviderUpdated(_priceProvider: address) <<VaultStorage>> -    <<event>> AllocateThresholdUpdated(_threshold: uint256) <<VaultStorage>> -    <<event>> RebaseThresholdUpdated(_threshold: uint256) <<VaultStorage>> -    <<event>> StrategistUpdated(_address: address) <<VaultStorage>> -    <<event>> MaxSupplyDiffChanged(maxSupplyDiff: uint256) <<VaultStorage>> -    <<event>> YieldDistribution(_to: address, _yield: uint256, _fee: uint256) <<VaultStorage>> -    <<event>> TrusteeFeeBpsChanged(_basis: uint256) <<VaultStorage>> -    <<event>> TrusteeAddressChanged(_address: address) <<VaultStorage>> -    <<event>> NetOusdMintForStrategyThresholdChanged(_threshold: uint256) <<VaultStorage>> -    <<event>> SwapperChanged(_address: address) <<VaultStorage>> -    <<event>> SwapAllowedUndervalueChanged(_basis: uint256) <<VaultStorage>> -    <<event>> SwapSlippageChanged(_asset: address, _basis: uint256) <<VaultStorage>> -    <<event>> Swapped(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _toAssetAmount: uint256) <<VaultStorage>> -    <<event>> DripperChanged(_dripper: address) <<VaultStorage>> -    <<event>> StrategyAddedToMintWhitelist(strategy: address) <<VaultStorage>> -    <<event>> StrategyRemovedFromMintWhitelist(strategy: address) <<VaultStorage>> -    <<event>> WithdrawalRequested(_withdrawer: address, _requestId: uint256, _amount: uint256, _queued: uint256) <<VaultStorage>> -    <<event>> WithdrawalClaimed(_withdrawer: address, _requestId: uint256, _amount: uint256) <<VaultStorage>> -    <<event>> WithdrawalClaimable(_claimable: uint256, _newClaimable: uint256) <<VaultStorage>> +286 + +OETHVaultAdmin +../contracts/vault/OETHVaultAdmin.sol + +Private: +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   _deprecated_rebaseHooksAddr: address <<VaultStorage>> +   _deprecated_uniswapAddr: address <<VaultStorage>> +   _deprecated_swapTokens: address[] <<VaultStorage>> +   __gap: uint256[44] <<VaultStorage>> +Internal: +   assets: mapping(address=>Asset) <<VaultStorage>> +   allAssets: address[] <<VaultStorage>> +   strategies: mapping(address=>Strategy) <<VaultStorage>> +   allStrategies: address[] <<VaultStorage>> +   oUSD: OUSD <<VaultStorage>> +   swapConfig: SwapConfig <<VaultStorage>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   priceProvider: address <<VaultStorage>> +   rebasePaused: bool <<VaultStorage>> +   capitalPaused: bool <<VaultStorage>> +   redeemFeeBps: uint256 <<VaultStorage>> +   vaultBuffer: uint256 <<VaultStorage>> +   autoAllocateThreshold: uint256 <<VaultStorage>> +   rebaseThreshold: uint256 <<VaultStorage>> +   adminImplPosition: bytes32 <<VaultStorage>> +   strategistAddr: address <<VaultStorage>> +   assetDefaultStrategies: mapping(address=>address) <<VaultStorage>> +   maxSupplyDiff: uint256 <<VaultStorage>> +   trusteeAddress: address <<VaultStorage>> +   trusteeFeeBps: uint256 <<VaultStorage>> +   MINT_MINIMUM_UNIT_PRICE: uint256 <<VaultStorage>> +   ousdMetaStrategy: address <<VaultStorage>> +   netOusdMintedForStrategy: int256 <<VaultStorage>> +   netOusdMintForStrategyThreshold: uint256 <<VaultStorage>> +   MIN_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   MAX_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   isMintWhitelistedStrategy: mapping(address=>bool) <<VaultStorage>> +   dripper: address <<VaultStorage>> +   withdrawalQueueMetadata: WithdrawalQueueMetadata <<VaultStorage>> +   withdrawalRequests: mapping(uint256=>WithdrawalRequest) <<VaultStorage>> +   withdrawalClaimDelay: uint256 <<VaultStorage>> +   weth: address <<OETHVaultAdmin>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _swapCollateral(address, address, uint256, uint256, bytes): uint256 <<OETHVaultAdmin>> +    _depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> +    _withdrawFromStrategy(_recipient: address, _strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> +    _withdrawAllFromStrategy(_strategyAddr: address) <<OETHVaultAdmin>> +    _withdrawAllFromStrategies() <<OETHVaultAdmin>> +    _cacheDecimals(token: address) <<VaultAdmin>> +    _wethAvailable(): (wethAvailable: uint256) <<OETHVaultAdmin>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    setAdminImpl(newImpl: address) <<onlyGovernor>> <<VaultStorage>> +    setPriceProvider(_priceProvider: address) <<onlyGovernor>> <<VaultAdmin>> +    setRedeemFeeBps(_redeemFeeBps: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setVaultBuffer(_vaultBuffer: uint256) <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    setAutoAllocateThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setRebaseThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setStrategistAddr(_address: address) <<onlyGovernor>> <<VaultAdmin>> +    setAssetDefaultStrategy(_asset: address, _strategy: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    setNetOusdMintForStrategyThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setDripper(_dripper: address) <<onlyGovernor>> <<VaultAdmin>> +    setWithdrawalClaimDelay(_delay: uint256) <<onlyGovernor>> <<VaultAdmin>> +    swapCollateral(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _minToAssetAmount: uint256, _data: bytes): (toAssetAmount: uint256) <<nonReentrant, onlyGovernorOrStrategist>> <<VaultAdmin>> +    setSwapper(_swapperAddr: address) <<onlyGovernor>> <<VaultAdmin>> +    swapper(): (swapper_: address) <<VaultAdmin>> +    setSwapAllowedUndervalue(_basis: uint16) <<onlyGovernor>> <<VaultAdmin>> +    allowedSwapUndervalue(): (value: uint256) <<VaultAdmin>> +    setOracleSlippage(_asset: address, _allowedOracleSlippageBps: uint16) <<onlyGovernor>> <<VaultAdmin>> +    supportAsset(_asset: address, _unitConversion: uint8) <<onlyGovernor>> <<VaultAdmin>> +    removeAsset(_asset: address) <<onlyGovernor>> <<VaultAdmin>> +    cacheDecimals(_asset: address) <<onlyGovernor>> <<VaultAdmin>> +    approveStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> +    removeStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> +    depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> +    withdrawFromStrategy(_strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> +    setMaxSupplyDiff(_maxSupplyDiff: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setTrusteeAddress(_address: address) <<onlyGovernor>> <<VaultAdmin>> +    setTrusteeFeeBps(_basis: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setOusdMetaStrategy(_ousdMetaStrategy: address) <<onlyGovernor>> <<VaultAdmin>> +    pauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    unpauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    pauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    unpauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<VaultAdmin>> +    withdrawAllFromStrategy(_strategyAddr: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    withdrawAllFromStrategies() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    addStrategyToMintWhitelist(strategyAddr: address) <<onlyGovernor>> <<OETHVaultAdmin>> +    removeStrategyFromMintWhitelist(strategyAddr: address) <<onlyGovernor>> <<OETHVaultAdmin>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> AssetSupported(_asset: address) <<VaultStorage>> +    <<event>> AssetRemoved(_asset: address) <<VaultStorage>> +    <<event>> AssetDefaultStrategyUpdated(_asset: address, _strategy: address) <<VaultStorage>> +    <<event>> AssetAllocated(_asset: address, _strategy: address, _amount: uint256) <<VaultStorage>> +    <<event>> StrategyApproved(_addr: address) <<VaultStorage>> +    <<event>> StrategyRemoved(_addr: address) <<VaultStorage>> +    <<event>> Mint(_addr: address, _value: uint256) <<VaultStorage>> +    <<event>> Redeem(_addr: address, _value: uint256) <<VaultStorage>> +    <<event>> CapitalPaused() <<VaultStorage>> +    <<event>> CapitalUnpaused() <<VaultStorage>> +    <<event>> RebasePaused() <<VaultStorage>> +    <<event>> RebaseUnpaused() <<VaultStorage>> +    <<event>> VaultBufferUpdated(_vaultBuffer: uint256) <<VaultStorage>> +    <<event>> OusdMetaStrategyUpdated(_ousdMetaStrategy: address) <<VaultStorage>> +    <<event>> RedeemFeeUpdated(_redeemFeeBps: uint256) <<VaultStorage>> +    <<event>> PriceProviderUpdated(_priceProvider: address) <<VaultStorage>> +    <<event>> AllocateThresholdUpdated(_threshold: uint256) <<VaultStorage>> +    <<event>> RebaseThresholdUpdated(_threshold: uint256) <<VaultStorage>> +    <<event>> StrategistUpdated(_address: address) <<VaultStorage>> +    <<event>> MaxSupplyDiffChanged(maxSupplyDiff: uint256) <<VaultStorage>> +    <<event>> YieldDistribution(_to: address, _yield: uint256, _fee: uint256) <<VaultStorage>> +    <<event>> TrusteeFeeBpsChanged(_basis: uint256) <<VaultStorage>> +    <<event>> TrusteeAddressChanged(_address: address) <<VaultStorage>> +    <<event>> NetOusdMintForStrategyThresholdChanged(_threshold: uint256) <<VaultStorage>> +    <<event>> SwapperChanged(_address: address) <<VaultStorage>> +    <<event>> SwapAllowedUndervalueChanged(_basis: uint256) <<VaultStorage>> +    <<event>> SwapSlippageChanged(_asset: address, _basis: uint256) <<VaultStorage>> +    <<event>> Swapped(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _toAssetAmount: uint256) <<VaultStorage>> +    <<event>> StrategyAddedToMintWhitelist(strategy: address) <<VaultStorage>> +    <<event>> StrategyRemovedFromMintWhitelist(strategy: address) <<VaultStorage>> +    <<event>> DripperChanged(_dripper: address) <<VaultStorage>> +    <<event>> WithdrawalRequested(_withdrawer: address, _requestId: uint256, _amount: uint256, _queued: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimed(_withdrawer: address, _requestId: uint256, _amount: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimable(_claimable: uint256, _newClaimable: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimDelayUpdated(_newDelay: uint256) <<VaultStorage>>    <<modifier>> initializer() <<Initializable>>    <<modifier>> onlyGovernor() <<Governable>>    <<modifier>> nonReentrant() <<Governable>> diff --git a/contracts/docs/OETHVaultCoreSquashed.svg b/contracts/docs/OETHVaultCoreSquashed.svg index 7ff1c7040e..4bb8488671 100644 --- a/contracts/docs/OETHVaultCoreSquashed.svg +++ b/contracts/docs/OETHVaultCoreSquashed.svg @@ -4,157 +4,158 @@ - - + + UmlClassDiagram - - + + -234 - -OETHVaultCore -../contracts/vault/OETHVaultCore.sol - -Private: -   initialized: bool <<Initializable>> -   initializing: bool <<Initializable>> -   ______gap: uint256[50] <<Initializable>> -   governorPosition: bytes32 <<Governable>> -   pendingGovernorPosition: bytes32 <<Governable>> -   reentryStatusPosition: bytes32 <<Governable>> -   _deprecated_rebaseHooksAddr: address <<VaultStorage>> -   _deprecated_uniswapAddr: address <<VaultStorage>> -   _deprecated_swapTokens: address[] <<VaultStorage>> -   __gap: uint256[45] <<VaultStorage>> -   __gap: uint256[50] <<OETHVaultCore>> -Internal: -   assets: mapping(address=>Asset) <<VaultStorage>> -   allAssets: address[] <<VaultStorage>> -   strategies: mapping(address=>Strategy) <<VaultStorage>> -   allStrategies: address[] <<VaultStorage>> -   oUSD: OUSD <<VaultStorage>> -   swapConfig: SwapConfig <<VaultStorage>> -   MAX_INT: uint256 <<VaultCore>> -Public: -   _NOT_ENTERED: uint256 <<Governable>> -   _ENTERED: uint256 <<Governable>> -   priceProvider: address <<VaultStorage>> -   rebasePaused: bool <<VaultStorage>> -   capitalPaused: bool <<VaultStorage>> -   redeemFeeBps: uint256 <<VaultStorage>> -   vaultBuffer: uint256 <<VaultStorage>> -   autoAllocateThreshold: uint256 <<VaultStorage>> -   rebaseThreshold: uint256 <<VaultStorage>> -   adminImplPosition: bytes32 <<VaultStorage>> -   strategistAddr: address <<VaultStorage>> -   assetDefaultStrategies: mapping(address=>address) <<VaultStorage>> -   maxSupplyDiff: uint256 <<VaultStorage>> -   trusteeAddress: address <<VaultStorage>> -   trusteeFeeBps: uint256 <<VaultStorage>> -   MINT_MINIMUM_UNIT_PRICE: uint256 <<VaultStorage>> -   ousdMetaStrategy: address <<VaultStorage>> -   netOusdMintedForStrategy: int256 <<VaultStorage>> -   netOusdMintForStrategyThreshold: uint256 <<VaultStorage>> -   MIN_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> -   MAX_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> -   isMintWhitelistedStrategy: mapping(address=>bool) <<VaultStorage>> -   dripper: address <<VaultStorage>> -   withdrawalQueueMetadata: WithdrawalQueueMetadata <<VaultStorage>> -   withdrawalRequests: mapping(uint256=>WithdrawalRequest) <<VaultStorage>> -   CLAIM_DELAY: uint256 <<OETHVaultCore>> -   weth: address <<OETHVaultCore>> -   wethAssetIndex: uint256 <<OETHVaultCore>> - -Private: -    abs(x: int256): uint256 <<VaultCore>> -Internal: -    _governor(): (governorOut: address) <<Governable>> -    _pendingGovernor(): (pendingGovernor: address) <<Governable>> -    _setGovernor(newGovernor: address) <<Governable>> -    _setPendingGovernor(newGovernor: address) <<Governable>> -    _changeGovernor(_newGovernor: address) <<Governable>> -    _mint(_asset: address, _amount: uint256, _minimumOusdAmount: uint256) <<OETHVaultCore>> -    _redeem(_amount: uint256, _minimumUnitAmount: uint256) <<OETHVaultCore>> -    _postRedeem(_amount: uint256) <<VaultCore>> -    _allocate() <<OETHVaultCore>> -    _rebase(): uint256 <<whenNotRebasePaused>> <<VaultCore>> -    _totalValue(): (value: uint256) <<OETHVaultCore>> -    _totalValueInVault(): (value: uint256) <<OETHVaultCore>> -    _totalValueInStrategies(): (value: uint256) <<VaultCore>> -    _totalValueInStrategy(_strategyAddr: address): (value: uint256) <<VaultCore>> -    _checkBalance(_asset: address): (balance: uint256) <<OETHVaultCore>> -    _calculateRedeemOutputs(_amount: uint256): (outputs: uint256[]) <<OETHVaultCore>> -    _toUnits(_raw: uint256, _asset: address): uint256 <<VaultCore>> -    _toUnitPrice(_asset: address, isMint: bool): (price: uint256) <<VaultCore>> -    _getDecimals(_asset: address): (decimals: uint256) <<VaultCore>> -    _claimWithdrawal(requestId: uint256): (amount: uint256) <<OETHVaultCore>> -    _addWithdrawalQueueLiquidity(): (addedClaimable: uint256) <<OETHVaultCore>> -    _wethAvailable(): (wethAvailable: uint256) <<OETHVaultCore>> -External: -    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> -    claimGovernance() <<Governable>> -    setAdminImpl(newImpl: address) <<onlyGovernor>> <<VaultStorage>> -    initialize(_priceProvider: address, _oToken: address) <<onlyGovernor, initializer>> <<VaultInitializer>> -    mint(_asset: address, _amount: uint256, _minimumOusdAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> -    mintForStrategy(_amount: uint256) <<whenNotCapitalPaused, onlyOusdMetaStrategy>> <<VaultCore>> -    redeem(_amount: uint256, _minimumUnitAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> -    burnForStrategy(_amount: uint256) <<whenNotCapitalPaused, onlyOusdMetaStrategy>> <<VaultCore>> -    redeemAll(_minimumUnitAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> -    allocate() <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> -    rebase() <<nonReentrant>> <<VaultCore>> -    totalValue(): (value: uint256) <<VaultCore>> -    checkBalance(_asset: address): uint256 <<VaultCore>> -    calculateRedeemOutputs(_amount: uint256): uint256[] <<VaultCore>> -    priceUnitMint(asset: address): (price: uint256) <<VaultCore>> -    priceUnitRedeem(asset: address): (price: uint256) <<VaultCore>> -    getAllAssets(): address[] <<VaultCore>> -    getStrategyCount(): uint256 <<VaultCore>> -    getAllStrategies(): address[] <<VaultCore>> -    isSupportedAsset(_asset: address): bool <<VaultCore>> -    null() <<VaultCore>> -    cacheWETHAssetIndex() <<onlyGovernor>> <<OETHVaultCore>> -    requestWithdrawal(_amount: uint256): (requestId: uint256, queued: uint256) <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> -    claimWithdrawal(_requestId: uint256): (amount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> -    claimWithdrawals(_requestIds: uint256[]): (amounts: uint256[], totalAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> -    addWithdrawalQueueLiquidity() <<OETHVaultCore>> -Public: -    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> -    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> -    <<event>> AssetSupported(_asset: address) <<VaultStorage>> -    <<event>> AssetRemoved(_asset: address) <<VaultStorage>> -    <<event>> AssetDefaultStrategyUpdated(_asset: address, _strategy: address) <<VaultStorage>> -    <<event>> AssetAllocated(_asset: address, _strategy: address, _amount: uint256) <<VaultStorage>> -    <<event>> StrategyApproved(_addr: address) <<VaultStorage>> -    <<event>> StrategyRemoved(_addr: address) <<VaultStorage>> -    <<event>> Mint(_addr: address, _value: uint256) <<VaultStorage>> -    <<event>> Redeem(_addr: address, _value: uint256) <<VaultStorage>> -    <<event>> CapitalPaused() <<VaultStorage>> -    <<event>> CapitalUnpaused() <<VaultStorage>> -    <<event>> RebasePaused() <<VaultStorage>> -    <<event>> RebaseUnpaused() <<VaultStorage>> -    <<event>> VaultBufferUpdated(_vaultBuffer: uint256) <<VaultStorage>> -    <<event>> OusdMetaStrategyUpdated(_ousdMetaStrategy: address) <<VaultStorage>> -    <<event>> RedeemFeeUpdated(_redeemFeeBps: uint256) <<VaultStorage>> -    <<event>> PriceProviderUpdated(_priceProvider: address) <<VaultStorage>> -    <<event>> AllocateThresholdUpdated(_threshold: uint256) <<VaultStorage>> -    <<event>> RebaseThresholdUpdated(_threshold: uint256) <<VaultStorage>> -    <<event>> StrategistUpdated(_address: address) <<VaultStorage>> -    <<event>> MaxSupplyDiffChanged(maxSupplyDiff: uint256) <<VaultStorage>> -    <<event>> YieldDistribution(_to: address, _yield: uint256, _fee: uint256) <<VaultStorage>> -    <<event>> TrusteeFeeBpsChanged(_basis: uint256) <<VaultStorage>> -    <<event>> TrusteeAddressChanged(_address: address) <<VaultStorage>> -    <<event>> NetOusdMintForStrategyThresholdChanged(_threshold: uint256) <<VaultStorage>> -    <<event>> SwapperChanged(_address: address) <<VaultStorage>> -    <<event>> SwapAllowedUndervalueChanged(_basis: uint256) <<VaultStorage>> -    <<event>> SwapSlippageChanged(_asset: address, _basis: uint256) <<VaultStorage>> -    <<event>> Swapped(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _toAssetAmount: uint256) <<VaultStorage>> -    <<event>> DripperChanged(_dripper: address) <<VaultStorage>> -    <<event>> StrategyAddedToMintWhitelist(strategy: address) <<VaultStorage>> -    <<event>> StrategyRemovedFromMintWhitelist(strategy: address) <<VaultStorage>> -    <<event>> WithdrawalRequested(_withdrawer: address, _requestId: uint256, _amount: uint256, _queued: uint256) <<VaultStorage>> -    <<event>> WithdrawalClaimed(_withdrawer: address, _requestId: uint256, _amount: uint256) <<VaultStorage>> -    <<event>> WithdrawalClaimable(_claimable: uint256, _newClaimable: uint256) <<VaultStorage>> +287 + +OETHVaultCore +../contracts/vault/OETHVaultCore.sol + +Private: +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   _deprecated_rebaseHooksAddr: address <<VaultStorage>> +   _deprecated_uniswapAddr: address <<VaultStorage>> +   _deprecated_swapTokens: address[] <<VaultStorage>> +   __gap: uint256[44] <<VaultStorage>> +   __gap: uint256[50] <<OETHVaultCore>> +Internal: +   assets: mapping(address=>Asset) <<VaultStorage>> +   allAssets: address[] <<VaultStorage>> +   strategies: mapping(address=>Strategy) <<VaultStorage>> +   allStrategies: address[] <<VaultStorage>> +   oUSD: OUSD <<VaultStorage>> +   swapConfig: SwapConfig <<VaultStorage>> +   MAX_INT: uint256 <<VaultCore>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   priceProvider: address <<VaultStorage>> +   rebasePaused: bool <<VaultStorage>> +   capitalPaused: bool <<VaultStorage>> +   redeemFeeBps: uint256 <<VaultStorage>> +   vaultBuffer: uint256 <<VaultStorage>> +   autoAllocateThreshold: uint256 <<VaultStorage>> +   rebaseThreshold: uint256 <<VaultStorage>> +   adminImplPosition: bytes32 <<VaultStorage>> +   strategistAddr: address <<VaultStorage>> +   assetDefaultStrategies: mapping(address=>address) <<VaultStorage>> +   maxSupplyDiff: uint256 <<VaultStorage>> +   trusteeAddress: address <<VaultStorage>> +   trusteeFeeBps: uint256 <<VaultStorage>> +   MINT_MINIMUM_UNIT_PRICE: uint256 <<VaultStorage>> +   ousdMetaStrategy: address <<VaultStorage>> +   netOusdMintedForStrategy: int256 <<VaultStorage>> +   netOusdMintForStrategyThreshold: uint256 <<VaultStorage>> +   MIN_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   MAX_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   isMintWhitelistedStrategy: mapping(address=>bool) <<VaultStorage>> +   dripper: address <<VaultStorage>> +   withdrawalQueueMetadata: WithdrawalQueueMetadata <<VaultStorage>> +   withdrawalRequests: mapping(uint256=>WithdrawalRequest) <<VaultStorage>> +   withdrawalClaimDelay: uint256 <<VaultStorage>> +   weth: address <<OETHVaultCore>> +   wethAssetIndex: uint256 <<OETHVaultCore>> + +Private: +    abs(x: int256): uint256 <<VaultCore>> +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _mint(_asset: address, _amount: uint256, _minimumOusdAmount: uint256) <<OETHVaultCore>> +    _redeem(_amount: uint256, _minimumUnitAmount: uint256) <<OETHVaultCore>> +    _postRedeem(_amount: uint256) <<VaultCore>> +    _allocate() <<OETHVaultCore>> +    _rebase(): uint256 <<whenNotRebasePaused>> <<VaultCore>> +    _totalValue(): (value: uint256) <<OETHVaultCore>> +    _totalValueInVault(): (value: uint256) <<OETHVaultCore>> +    _totalValueInStrategies(): (value: uint256) <<VaultCore>> +    _totalValueInStrategy(_strategyAddr: address): (value: uint256) <<VaultCore>> +    _checkBalance(_asset: address): (balance: uint256) <<OETHVaultCore>> +    _calculateRedeemOutputs(_amount: uint256): (outputs: uint256[]) <<OETHVaultCore>> +    _toUnits(_raw: uint256, _asset: address): uint256 <<VaultCore>> +    _toUnitPrice(_asset: address, isMint: bool): (price: uint256) <<VaultCore>> +    _getDecimals(_asset: address): (decimals: uint256) <<VaultCore>> +    _claimWithdrawal(requestId: uint256): (amount: uint256) <<OETHVaultCore>> +    _addWithdrawalQueueLiquidity(): (addedClaimable: uint256) <<OETHVaultCore>> +    _wethAvailable(): (wethAvailable: uint256) <<OETHVaultCore>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    setAdminImpl(newImpl: address) <<onlyGovernor>> <<VaultStorage>> +    initialize(_priceProvider: address, _oToken: address) <<onlyGovernor, initializer>> <<VaultInitializer>> +    mint(_asset: address, _amount: uint256, _minimumOusdAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> +    mintForStrategy(amount: uint256) <<whenNotCapitalPaused>> <<OETHVaultCore>> +    redeem(_amount: uint256, _minimumUnitAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> +    burnForStrategy(amount: uint256) <<whenNotCapitalPaused>> <<OETHVaultCore>> +    redeemAll(_minimumUnitAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> +    allocate() <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> +    rebase() <<nonReentrant>> <<VaultCore>> +    totalValue(): (value: uint256) <<VaultCore>> +    checkBalance(_asset: address): uint256 <<VaultCore>> +    calculateRedeemOutputs(_amount: uint256): uint256[] <<VaultCore>> +    priceUnitMint(asset: address): (price: uint256) <<VaultCore>> +    priceUnitRedeem(asset: address): (price: uint256) <<VaultCore>> +    getAllAssets(): address[] <<VaultCore>> +    getStrategyCount(): uint256 <<VaultCore>> +    getAllStrategies(): address[] <<VaultCore>> +    isSupportedAsset(_asset: address): bool <<VaultCore>> +    null() <<VaultCore>> +    cacheWETHAssetIndex() <<onlyGovernor>> <<OETHVaultCore>> +    requestWithdrawal(_amount: uint256): (requestId: uint256, queued: uint256) <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> +    claimWithdrawal(_requestId: uint256): (amount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> +    claimWithdrawals(_requestIds: uint256[]): (amounts: uint256[], totalAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>> +    addWithdrawalQueueLiquidity() <<OETHVaultCore>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> AssetSupported(_asset: address) <<VaultStorage>> +    <<event>> AssetRemoved(_asset: address) <<VaultStorage>> +    <<event>> AssetDefaultStrategyUpdated(_asset: address, _strategy: address) <<VaultStorage>> +    <<event>> AssetAllocated(_asset: address, _strategy: address, _amount: uint256) <<VaultStorage>> +    <<event>> StrategyApproved(_addr: address) <<VaultStorage>> +    <<event>> StrategyRemoved(_addr: address) <<VaultStorage>> +    <<event>> Mint(_addr: address, _value: uint256) <<VaultStorage>> +    <<event>> Redeem(_addr: address, _value: uint256) <<VaultStorage>> +    <<event>> CapitalPaused() <<VaultStorage>> +    <<event>> CapitalUnpaused() <<VaultStorage>> +    <<event>> RebasePaused() <<VaultStorage>> +    <<event>> RebaseUnpaused() <<VaultStorage>> +    <<event>> VaultBufferUpdated(_vaultBuffer: uint256) <<VaultStorage>> +    <<event>> OusdMetaStrategyUpdated(_ousdMetaStrategy: address) <<VaultStorage>> +    <<event>> RedeemFeeUpdated(_redeemFeeBps: uint256) <<VaultStorage>> +    <<event>> PriceProviderUpdated(_priceProvider: address) <<VaultStorage>> +    <<event>> AllocateThresholdUpdated(_threshold: uint256) <<VaultStorage>> +    <<event>> RebaseThresholdUpdated(_threshold: uint256) <<VaultStorage>> +    <<event>> StrategistUpdated(_address: address) <<VaultStorage>> +    <<event>> MaxSupplyDiffChanged(maxSupplyDiff: uint256) <<VaultStorage>> +    <<event>> YieldDistribution(_to: address, _yield: uint256, _fee: uint256) <<VaultStorage>> +    <<event>> TrusteeFeeBpsChanged(_basis: uint256) <<VaultStorage>> +    <<event>> TrusteeAddressChanged(_address: address) <<VaultStorage>> +    <<event>> NetOusdMintForStrategyThresholdChanged(_threshold: uint256) <<VaultStorage>> +    <<event>> SwapperChanged(_address: address) <<VaultStorage>> +    <<event>> SwapAllowedUndervalueChanged(_basis: uint256) <<VaultStorage>> +    <<event>> SwapSlippageChanged(_asset: address, _basis: uint256) <<VaultStorage>> +    <<event>> Swapped(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _toAssetAmount: uint256) <<VaultStorage>> +    <<event>> StrategyAddedToMintWhitelist(strategy: address) <<VaultStorage>> +    <<event>> StrategyRemovedFromMintWhitelist(strategy: address) <<VaultStorage>> +    <<event>> DripperChanged(_dripper: address) <<VaultStorage>> +    <<event>> WithdrawalRequested(_withdrawer: address, _requestId: uint256, _amount: uint256, _queued: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimed(_withdrawer: address, _requestId: uint256, _amount: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimable(_claimable: uint256, _newClaimable: uint256) <<VaultStorage>> +    <<event>> WithdrawalClaimDelayUpdated(_newDelay: uint256) <<VaultStorage>>    <<modifier>> initializer() <<Initializable>>    <<modifier>> onlyGovernor() <<Governable>>    <<modifier>> nonReentrant() <<Governable>> diff --git a/contracts/docs/OETHVaultHierarchy.svg b/contracts/docs/OETHVaultHierarchy.svg deleted file mode 100644 index ce6caafd6c..0000000000 --- a/contracts/docs/OETHVaultHierarchy.svg +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - -UmlClassDiagram - - - -20 - -Governable -../contracts/governance/Governable.sol - - - -215 - -OUSD -../contracts/token/OUSD.sol - - - -215->20 - - - - - -225 - -<<Abstract>> -Initializable -../contracts/utils/Initializable.sol - - - -215->225 - - - - - -228 - -<<Abstract>> -InitializableERC20Detailed -../contracts/utils/InitializableERC20Detailed.sol - - - -215->228 - - - - - -233 - -OETHVaultAdmin -../contracts/vault/OETHVaultAdmin.sol - - - -237 - -VaultAdmin -../contracts/vault/VaultAdmin.sol - - - -233->237 - - - - - -234 - -OETHVaultCore -../contracts/vault/OETHVaultCore.sol - - - -238 - -VaultCore -../contracts/vault/VaultCore.sol - - - -234->238 - - - - - -240 - -VaultStorage -../contracts/vault/VaultStorage.sol - - - -237->240 - - - - - -239 - -VaultInitializer -../contracts/vault/VaultInitializer.sol - - - -238->239 - - - - - -239->215 - - - - - -239->240 - - - - - -240->20 - - - - - -240->215 - - - - - -240->225 - - - - - diff --git a/contracts/docs/OETHVaultStorage.svg b/contracts/docs/OETHVaultStorage.svg index 8fb932c367..e425f8712d 100644 --- a/contracts/docs/OETHVaultStorage.svg +++ b/contracts/docs/OETHVaultStorage.svg @@ -4,268 +4,272 @@ - - + + StorageDiagram - + 8 - -OETHVaultCore <<Contract>> - -slot - -0 - -1-50 - -51 - -52 - -53 - -54 - -55 - -56 - -57 - -58 - -59 - -60 - -61 - -62 - -63 - -64 - -65 - -66 - -67 - -68 - -69 - -70 - -71 - -72 - -73 - -74 - -75-76 - -77 - -78-122 - -123 - -124-173 - -type: <inherited contract>.variable (bytes) - -unallocated (30) - -bool: Initializable.initializing (1) - -bool: Initializable.initialized (1) - -uint256[50]: Initializable.______gap (1600) - -mapping(address=>Asset): VaultStorage.assets (32) - -address[]: VaultStorage.allAssets (32) - -mapping(address=>Strategy): VaultStorage.strategies (32) - -address[]: VaultStorage.allStrategies (32) - -unallocated (10) - -bool: VaultStorage.capitalPaused (1) - -bool: VaultStorage.rebasePaused (1) - -address: VaultStorage.priceProvider (20) - -uint256: VaultStorage.redeemFeeBps (32) - -uint256: VaultStorage.vaultBuffer (32) - -uint256: VaultStorage.autoAllocateThreshold (32) - -uint256: VaultStorage.rebaseThreshold (32) - -unallocated (12) - -OUSD: VaultStorage.oUSD (20) - -unallocated (12) - -address: VaultStorage._deprecated_rebaseHooksAddr (20) - -unallocated (12) - -address: VaultStorage._deprecated_uniswapAddr (20) - -unallocated (12) - -address: VaultStorage.strategistAddr (20) - -mapping(address=>address): VaultStorage.assetDefaultStrategies (32) - -uint256: VaultStorage.maxSupplyDiff (32) - -unallocated (12) - -address: VaultStorage.trusteeAddress (20) - -uint256: VaultStorage.trusteeFeeBps (32) - -address[]: VaultStorage._deprecated_swapTokens (32) - -unallocated (12) - -address: VaultStorage.ousdMetaStrategy (20) - -int256: VaultStorage.netOusdMintedForStrategy (32) - -uint256: VaultStorage.netOusdMintForStrategyThreshold (32) - -SwapConfig: VaultStorage.swapConfig (32) - -mapping(address=>bool): VaultStorage.isMintWhitelistedStrategy (32) - -unallocated (12) - -address: VaultStorage.dripper (20) - -WithdrawalQueueMetadata: VaultStorage.withdrawalQueueMetadata (64) - -mapping(uint256=>WithdrawalRequest): VaultStorage.withdrawalRequests (32) - -uint256[45]: VaultStorage.__gap (1440) - -uint256: wethAssetIndex (32) - -uint256[50]: __gap (1600) + +OETHVaultCore <<Contract>> + +slot + +0 + +1-50 + +51 + +52 + +53 + +54 + +55 + +56 + +57 + +58 + +59 + +60 + +61 + +62 + +63 + +64 + +65 + +66 + +67 + +68 + +69 + +70 + +71 + +72 + +73 + +74 + +75-76 + +77 + +78 + +79-122 + +123 + +124-173 + +type: <inherited contract>.variable (bytes) + +unallocated (30) + +bool: Initializable.initializing (1) + +bool: Initializable.initialized (1) + +uint256[50]: Initializable.______gap (1600) + +mapping(address=>Asset): VaultStorage.assets (32) + +address[]: VaultStorage.allAssets (32) + +mapping(address=>Strategy): VaultStorage.strategies (32) + +address[]: VaultStorage.allStrategies (32) + +unallocated (10) + +bool: VaultStorage.capitalPaused (1) + +bool: VaultStorage.rebasePaused (1) + +address: VaultStorage.priceProvider (20) + +uint256: VaultStorage.redeemFeeBps (32) + +uint256: VaultStorage.vaultBuffer (32) + +uint256: VaultStorage.autoAllocateThreshold (32) + +uint256: VaultStorage.rebaseThreshold (32) + +unallocated (12) + +OUSD: VaultStorage.oUSD (20) + +unallocated (12) + +address: VaultStorage._deprecated_rebaseHooksAddr (20) + +unallocated (12) + +address: VaultStorage._deprecated_uniswapAddr (20) + +unallocated (12) + +address: VaultStorage.strategistAddr (20) + +mapping(address=>address): VaultStorage.assetDefaultStrategies (32) + +uint256: VaultStorage.maxSupplyDiff (32) + +unallocated (12) + +address: VaultStorage.trusteeAddress (20) + +uint256: VaultStorage.trusteeFeeBps (32) + +address[]: VaultStorage._deprecated_swapTokens (32) + +unallocated (12) + +address: VaultStorage.ousdMetaStrategy (20) + +int256: VaultStorage.netOusdMintedForStrategy (32) + +uint256: VaultStorage.netOusdMintForStrategyThreshold (32) + +SwapConfig: VaultStorage.swapConfig (32) + +mapping(address=>bool): VaultStorage.isMintWhitelistedStrategy (32) + +unallocated (12) + +address: VaultStorage.dripper (20) + +WithdrawalQueueMetadata: VaultStorage.withdrawalQueueMetadata (64) + +mapping(uint256=>WithdrawalRequest): VaultStorage.withdrawalRequests (32) + +uint256: VaultStorage.withdrawalClaimDelay (32) + +uint256[44]: VaultStorage.__gap (1408) + +uint256: wethAssetIndex (32) + +uint256[50]: __gap (1600) 1 - -Asset <<Struct>> - -offset - -0 - -type: variable (bytes) - -unallocated (27) - -uint16: allowedOracleSlippageBps (2) - -uint8: decimals (1) - -UnitConversion: unitConversion (1) - -bool: isSupported (1) + +Asset <<Struct>> + +offset + +0 + +type: variable (bytes) + +unallocated (27) + +uint16: allowedOracleSlippageBps (2) + +uint8: decimals (1) + +UnitConversion: unitConversion (1) + +bool: isSupported (1) 8:8->1 - - + + 2 - -address[]: allAssets <<Array>> -0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 - -offset - -0 - -type: variable (bytes) - -unallocated (12) - -address (20) + +address[]: allAssets <<Array>> +0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) 8:10->2 - - + + 3 - -Strategy <<Struct>> - -offset - -0 - -1 - -type: variable (bytes) - -unallocated (31) - -bool: isSupported (1) - -uint256: _deprecated (32) + +Strategy <<Struct>> + +offset + +0 + +1 + +type: variable (bytes) + +unallocated (31) + +bool: isSupported (1) + +uint256: _deprecated (32) 8:13->3 - - + + 4 - -address[]: allStrategies <<Array>> -0x4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b8 - -offset - -0 - -type: variable (bytes) - -unallocated (12) - -address (20) + +address[]: allStrategies <<Array>> +0x4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b8 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) 8:15->4 - - + + @@ -288,8 +292,8 @@ 8:37->5 - - + + @@ -348,8 +352,8 @@ 8:50->7 - - + + diff --git a/contracts/docs/OSonicHarvesterHierarchy.svg b/contracts/docs/OSonicHarvesterHierarchy.svg new file mode 100644 index 0000000000..ad89bf857b --- /dev/null +++ b/contracts/docs/OSonicHarvesterHierarchy.svg @@ -0,0 +1,86 @@ + + + + + + +UmlClassDiagram + + + +21 + +Governable +../contracts/governance/Governable.sol + + + +26 + +Strategizable +../contracts/governance/Strategizable.sol + + + +26->21 + + + + + +39 + +OETHHarvesterSimple +../contracts/harvest/OETHHarvesterSimple.sol + + + +39->26 + + + + + +279 + +<<Abstract>> +Initializable +../contracts/utils/Initializable.sol + + + +39->279 + + + + + +40 + +OSonicHarvester +../contracts/harvest/OSonicHarvester.sol + + + +41 + +SuperOETHHarvester +../contracts/harvest/SuperOETHHarvester.sol + + + +40->41 + + + + + +41->39 + + + + + diff --git a/contracts/docs/OSonicHarvesterSquashed.svg b/contracts/docs/OSonicHarvesterSquashed.svg new file mode 100644 index 0000000000..ef34b5342e --- /dev/null +++ b/contracts/docs/OSonicHarvesterSquashed.svg @@ -0,0 +1,72 @@ + + + + + + +UmlClassDiagram + + + +40 + +OSonicHarvester +../contracts/harvest/OSonicHarvester.sol + +Private: +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   __gap: uint256[50] <<Strategizable>> +   ___gap: uint256[48] <<OETHHarvesterSimple>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   strategistAddr: address <<Strategizable>> +   wrappedNativeToken: address <<OETHHarvesterSimple>> +   dripper: address <<OETHHarvesterSimple>> +   supportedStrategies: mapping(address=>bool) <<OETHHarvesterSimple>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _setStrategistAddr(_address: address) <<Strategizable>> +    _harvestAndTransfer(_strategy: address) <<SuperOETHHarvester>> +    _setDripper(_dripper: address) <<OETHHarvesterSimple>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    setStrategistAddr(_address: address) <<onlyGovernor>> <<Strategizable>> +    initialize(_governor: address, _strategist: address, _dripper: address) <<initializer>> <<OETHHarvesterSimple>> +    harvestAndTransfer(_strategy: address) <<OETHHarvesterSimple>> +    harvestAndTransfer(_strategies: address[]) <<OETHHarvesterSimple>> +    setSupportedStrategy(_strategy: address, _isSupported: bool) <<onlyGovernorOrStrategist>> <<OETHHarvesterSimple>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernorOrStrategist>> <<OETHHarvesterSimple>> +    setDripper(_dripper: address) <<onlyGovernor>> <<OETHHarvesterSimple>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> StrategistUpdated(_address: address) <<Strategizable>> +    <<event>> Harvested(strategy: address, token: address, amount: uint256, receiver: address) <<OETHHarvesterSimple>> +    <<event>> SupportedStrategyUpdated(strategy: address, status: bool) <<OETHHarvesterSimple>> +    <<event>> DripperUpdated(dripper: address) <<OETHHarvesterSimple>> +    <<modifier>> initializer() <<Initializable>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    <<modifier>> onlyGovernorOrStrategist() <<Strategizable>> +    constructor() <<Governable>> +    governor(): address <<Governable>> +    isGovernor(): bool <<Governable>> +    constructor(_wrappedNativeToken: address) <<OSonicHarvester>> + + + diff --git a/contracts/docs/OSonicHarvesterStorage.svg b/contracts/docs/OSonicHarvesterStorage.svg new file mode 100644 index 0000000000..d8a60a18d9 --- /dev/null +++ b/contracts/docs/OSonicHarvesterStorage.svg @@ -0,0 +1,59 @@ + + + + + + +StorageDiagram + + + +1 + +OSonicHarvester <<Contract>> + +slot + +0 + +1-50 + +51 + +52-101 + +102 + +103 + +104-151 + +type: <inherited contract>.variable (bytes) + +unallocated (30) + +bool: Initializable.initializing (1) + +bool: Initializable.initialized (1) + +uint256[50]: Initializable.______gap (1600) + +unallocated (12) + +address: Strategizable.strategistAddr (20) + +uint256[50]: Strategizable.__gap (1600) + +unallocated (12) + +address: OETHHarvesterSimple.dripper (20) + +mapping(address=>bool): OETHHarvesterSimple.supportedStrategies (32) + +uint256[48]: OETHHarvesterSimple.___gap (1536) + + + diff --git a/contracts/docs/OSonicVaultAdminSquashed.svg b/contracts/docs/OSonicVaultAdminSquashed.svg index 22be0c6cef..ba51485b4e 100644 --- a/contracts/docs/OSonicVaultAdminSquashed.svg +++ b/contracts/docs/OSonicVaultAdminSquashed.svg @@ -4,116 +4,118 @@ - - + + UmlClassDiagram - - + + -273 - -OSonicVaultAdmin -../contracts/vault/OSonicVaultAdmin.sol - -Private: -   initialized: bool <<Initializable>> -   initializing: bool <<Initializable>> -   ______gap: uint256[50] <<Initializable>> -   governorPosition: bytes32 <<Governable>> -   pendingGovernorPosition: bytes32 <<Governable>> -   reentryStatusPosition: bytes32 <<Governable>> -   _deprecated_rebaseHooksAddr: address <<VaultStorage>> -   _deprecated_uniswapAddr: address <<VaultStorage>> -   _deprecated_swapTokens: address[] <<VaultStorage>> -   __gap: uint256[44] <<VaultStorage>> -Internal: -   assets: mapping(address=>Asset) <<VaultStorage>> -   allAssets: address[] <<VaultStorage>> -   strategies: mapping(address=>Strategy) <<VaultStorage>> -   allStrategies: address[] <<VaultStorage>> -   oUSD: OUSD <<VaultStorage>> -   swapConfig: SwapConfig <<VaultStorage>> -Public: -   _NOT_ENTERED: uint256 <<Governable>> -   _ENTERED: uint256 <<Governable>> -   priceProvider: address <<VaultStorage>> -   rebasePaused: bool <<VaultStorage>> -   capitalPaused: bool <<VaultStorage>> -   redeemFeeBps: uint256 <<VaultStorage>> -   vaultBuffer: uint256 <<VaultStorage>> -   autoAllocateThreshold: uint256 <<VaultStorage>> -   rebaseThreshold: uint256 <<VaultStorage>> -   adminImplPosition: bytes32 <<VaultStorage>> -   strategistAddr: address <<VaultStorage>> -   assetDefaultStrategies: mapping(address=>address) <<VaultStorage>> -   maxSupplyDiff: uint256 <<VaultStorage>> -   trusteeAddress: address <<VaultStorage>> -   trusteeFeeBps: uint256 <<VaultStorage>> -   MINT_MINIMUM_UNIT_PRICE: uint256 <<VaultStorage>> -   ousdMetaStrategy: address <<VaultStorage>> -   netOusdMintedForStrategy: int256 <<VaultStorage>> -   netOusdMintForStrategyThreshold: uint256 <<VaultStorage>> -   MIN_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> -   MAX_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> -   isMintWhitelistedStrategy: mapping(address=>bool) <<VaultStorage>> -   dripper: address <<VaultStorage>> -   withdrawalQueueMetadata: WithdrawalQueueMetadata <<VaultStorage>> -   withdrawalRequests: mapping(uint256=>WithdrawalRequest) <<VaultStorage>> -   withdrawalClaimDelay: uint256 <<VaultStorage>> -   weth: address <<OETHVaultAdmin>> - -Internal: -    _governor(): (governorOut: address) <<Governable>> -    _pendingGovernor(): (pendingGovernor: address) <<Governable>> -    _setGovernor(newGovernor: address) <<Governable>> -    _setPendingGovernor(newGovernor: address) <<Governable>> -    _changeGovernor(_newGovernor: address) <<Governable>> -    _swapCollateral(address, address, uint256, uint256, bytes): uint256 <<OETHVaultAdmin>> -    _depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> -    _withdrawFromStrategy(_recipient: address, _strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> -    _withdrawAllFromStrategy(_strategyAddr: address) <<OETHVaultAdmin>> -    _withdrawAllFromStrategies() <<OETHVaultAdmin>> -    _cacheDecimals(token: address) <<VaultAdmin>> -    _wethAvailable(): (wethAvailable: uint256) <<OETHVaultAdmin>> -External: -    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> -    claimGovernance() <<Governable>> -    setAdminImpl(newImpl: address) <<onlyGovernor>> <<VaultStorage>> -    setPriceProvider(_priceProvider: address) <<onlyGovernor>> <<VaultAdmin>> -    setRedeemFeeBps(_redeemFeeBps: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setVaultBuffer(_vaultBuffer: uint256) <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    setAutoAllocateThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setRebaseThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setStrategistAddr(_address: address) <<onlyGovernor>> <<VaultAdmin>> -    setAssetDefaultStrategy(_asset: address, _strategy: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    setNetOusdMintForStrategyThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setDripper(_dripper: address) <<onlyGovernor>> <<VaultAdmin>> -    setWithdrawalClaimDelay(_delay: uint256) <<onlyGovernor>> <<VaultAdmin>> -    swapCollateral(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _minToAssetAmount: uint256, _data: bytes): (toAssetAmount: uint256) <<nonReentrant, onlyGovernorOrStrategist>> <<VaultAdmin>> -    setSwapper(_swapperAddr: address) <<onlyGovernor>> <<VaultAdmin>> -    swapper(): (swapper_: address) <<VaultAdmin>> -    setSwapAllowedUndervalue(_basis: uint16) <<onlyGovernor>> <<VaultAdmin>> -    allowedSwapUndervalue(): (value: uint256) <<VaultAdmin>> -    setOracleSlippage(_asset: address, _allowedOracleSlippageBps: uint16) <<onlyGovernor>> <<VaultAdmin>> -    supportAsset(_asset: address, _unitConversion: uint8) <<onlyGovernor>> <<OSonicVaultAdmin>> -    removeAsset(_asset: address) <<onlyGovernor>> <<VaultAdmin>> -    cacheDecimals(_asset: address) <<onlyGovernor>> <<VaultAdmin>> -    approveStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> -    removeStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> -    depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> -    withdrawFromStrategy(_strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> -    setMaxSupplyDiff(_maxSupplyDiff: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setTrusteeAddress(_address: address) <<onlyGovernor>> <<VaultAdmin>> -    setTrusteeFeeBps(_basis: uint256) <<onlyGovernor>> <<VaultAdmin>> -    setOusdMetaStrategy(_ousdMetaStrategy: address) <<onlyGovernor>> <<VaultAdmin>> -    pauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    unpauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    pauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    unpauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<VaultAdmin>> -    withdrawAllFromStrategy(_strategyAddr: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> -    withdrawAllFromStrategies() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +289 + +OSonicVaultAdmin +../contracts/vault/OSonicVaultAdmin.sol + +Private: +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   _deprecated_rebaseHooksAddr: address <<VaultStorage>> +   _deprecated_uniswapAddr: address <<VaultStorage>> +   _deprecated_swapTokens: address[] <<VaultStorage>> +   __gap: uint256[44] <<VaultStorage>> +Internal: +   assets: mapping(address=>Asset) <<VaultStorage>> +   allAssets: address[] <<VaultStorage>> +   strategies: mapping(address=>Strategy) <<VaultStorage>> +   allStrategies: address[] <<VaultStorage>> +   oUSD: OUSD <<VaultStorage>> +   swapConfig: SwapConfig <<VaultStorage>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   priceProvider: address <<VaultStorage>> +   rebasePaused: bool <<VaultStorage>> +   capitalPaused: bool <<VaultStorage>> +   redeemFeeBps: uint256 <<VaultStorage>> +   vaultBuffer: uint256 <<VaultStorage>> +   autoAllocateThreshold: uint256 <<VaultStorage>> +   rebaseThreshold: uint256 <<VaultStorage>> +   adminImplPosition: bytes32 <<VaultStorage>> +   strategistAddr: address <<VaultStorage>> +   assetDefaultStrategies: mapping(address=>address) <<VaultStorage>> +   maxSupplyDiff: uint256 <<VaultStorage>> +   trusteeAddress: address <<VaultStorage>> +   trusteeFeeBps: uint256 <<VaultStorage>> +   MINT_MINIMUM_UNIT_PRICE: uint256 <<VaultStorage>> +   ousdMetaStrategy: address <<VaultStorage>> +   netOusdMintedForStrategy: int256 <<VaultStorage>> +   netOusdMintForStrategyThreshold: uint256 <<VaultStorage>> +   MIN_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   MAX_UNIT_PRICE_DRIFT: uint256 <<VaultStorage>> +   isMintWhitelistedStrategy: mapping(address=>bool) <<VaultStorage>> +   dripper: address <<VaultStorage>> +   withdrawalQueueMetadata: WithdrawalQueueMetadata <<VaultStorage>> +   withdrawalRequests: mapping(uint256=>WithdrawalRequest) <<VaultStorage>> +   withdrawalClaimDelay: uint256 <<VaultStorage>> +   weth: address <<OETHVaultAdmin>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _swapCollateral(address, address, uint256, uint256, bytes): uint256 <<OETHVaultAdmin>> +    _depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> +    _withdrawFromStrategy(_recipient: address, _strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<OETHVaultAdmin>> +    _withdrawAllFromStrategy(_strategyAddr: address) <<OETHVaultAdmin>> +    _withdrawAllFromStrategies() <<OETHVaultAdmin>> +    _cacheDecimals(token: address) <<VaultAdmin>> +    _wethAvailable(): (wethAvailable: uint256) <<OETHVaultAdmin>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    setAdminImpl(newImpl: address) <<onlyGovernor>> <<VaultStorage>> +    setPriceProvider(_priceProvider: address) <<onlyGovernor>> <<VaultAdmin>> +    setRedeemFeeBps(_redeemFeeBps: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setVaultBuffer(_vaultBuffer: uint256) <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    setAutoAllocateThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setRebaseThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setStrategistAddr(_address: address) <<onlyGovernor>> <<VaultAdmin>> +    setAssetDefaultStrategy(_asset: address, _strategy: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    setNetOusdMintForStrategyThreshold(_threshold: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setDripper(_dripper: address) <<onlyGovernor>> <<VaultAdmin>> +    setWithdrawalClaimDelay(_delay: uint256) <<onlyGovernor>> <<VaultAdmin>> +    swapCollateral(_fromAsset: address, _toAsset: address, _fromAssetAmount: uint256, _minToAssetAmount: uint256, _data: bytes): (toAssetAmount: uint256) <<nonReentrant, onlyGovernorOrStrategist>> <<VaultAdmin>> +    setSwapper(_swapperAddr: address) <<onlyGovernor>> <<VaultAdmin>> +    swapper(): (swapper_: address) <<VaultAdmin>> +    setSwapAllowedUndervalue(_basis: uint16) <<onlyGovernor>> <<VaultAdmin>> +    allowedSwapUndervalue(): (value: uint256) <<VaultAdmin>> +    setOracleSlippage(_asset: address, _allowedOracleSlippageBps: uint16) <<onlyGovernor>> <<VaultAdmin>> +    supportAsset(_asset: address, _unitConversion: uint8) <<onlyGovernor>> <<OSonicVaultAdmin>> +    removeAsset(_asset: address) <<onlyGovernor>> <<VaultAdmin>> +    cacheDecimals(_asset: address) <<onlyGovernor>> <<VaultAdmin>> +    approveStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> +    removeStrategy(_addr: address) <<onlyGovernor>> <<VaultAdmin>> +    depositToStrategy(_strategyToAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> +    withdrawFromStrategy(_strategyFromAddress: address, _assets: address[], _amounts: uint256[]) <<onlyGovernorOrStrategist, nonReentrant>> <<VaultAdmin>> +    setMaxSupplyDiff(_maxSupplyDiff: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setTrusteeAddress(_address: address) <<onlyGovernor>> <<VaultAdmin>> +    setTrusteeFeeBps(_basis: uint256) <<onlyGovernor>> <<VaultAdmin>> +    setOusdMetaStrategy(_ousdMetaStrategy: address) <<onlyGovernor>> <<VaultAdmin>> +    pauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    unpauseRebase() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    pauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    unpauseCapital() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<VaultAdmin>> +    withdrawAllFromStrategy(_strategyAddr: address) <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    withdrawAllFromStrategies() <<onlyGovernorOrStrategist>> <<VaultAdmin>> +    addStrategyToMintWhitelist(strategyAddr: address) <<onlyGovernor>> <<OETHVaultAdmin>> +    removeStrategyFromMintWhitelist(strategyAddr: address) <<onlyGovernor>> <<OETHVaultAdmin>> Public:    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>>    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> diff --git a/contracts/docs/OSonicVaultCoreSquashed.svg b/contracts/docs/OSonicVaultCoreSquashed.svg index 51ac9515a6..9d467a3367 100644 --- a/contracts/docs/OSonicVaultCoreSquashed.svg +++ b/contracts/docs/OSonicVaultCoreSquashed.svg @@ -9,9 +9,9 @@ UmlClassDiagram - + -274 +290 OSonicVaultCore ../contracts/vault/OSonicVaultCore.sol @@ -97,9 +97,9 @@    setAdminImpl(newImpl: address) <<onlyGovernor>> <<VaultStorage>>    initialize(_priceProvider: address, _oToken: address) <<onlyGovernor, initializer>> <<VaultInitializer>>    mint(_asset: address, _amount: uint256, _minimumOusdAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>> -    mintForStrategy(_amount: uint256) <<whenNotCapitalPaused, onlyOusdMetaStrategy>> <<VaultCore>> +    mintForStrategy(amount: uint256) <<whenNotCapitalPaused>> <<OETHVaultCore>>    redeem(uint256, uint256) <<OSonicVaultCore>> -    burnForStrategy(_amount: uint256) <<whenNotCapitalPaused, onlyOusdMetaStrategy>> <<VaultCore>> +    burnForStrategy(amount: uint256) <<whenNotCapitalPaused>> <<OETHVaultCore>>    redeemAll(_minimumUnitAmount: uint256) <<whenNotCapitalPaused, nonReentrant>> <<VaultCore>>    allocate() <<whenNotCapitalPaused, nonReentrant>> <<OETHVaultCore>>    rebase() <<nonReentrant>> <<VaultCore>> diff --git a/contracts/docs/SonicCurveAMOStrategyHierarchy.svg b/contracts/docs/SonicCurveAMOStrategyHierarchy.svg new file mode 100644 index 0000000000..d2c3db1806 --- /dev/null +++ b/contracts/docs/SonicCurveAMOStrategyHierarchy.svg @@ -0,0 +1,74 @@ + + + + + + +UmlClassDiagram + + + +21 + +Governable +../contracts/governance/Governable.sol + + + +229 + +BaseCurveAMOStrategy +../contracts/strategies/BaseCurveAMOStrategy.sol + + + +290 + +<<Abstract>> +InitializableAbstractStrategy +../contracts/utils/InitializableAbstractStrategy.sol + + + +229->290 + + + + + +410 + +SonicCurveAMOStrategy +../contracts/strategies/sonic/SonicCurveAMOStrategy.sol + + + +410->229 + + + + + +289 + +<<Abstract>> +Initializable +../contracts/utils/Initializable.sol + + + +290->21 + + + + + +290->289 + + + + + diff --git a/contracts/docs/SonicCurveAMOStrategySquashed.svg b/contracts/docs/SonicCurveAMOStrategySquashed.svg new file mode 100644 index 0000000000..bf0f450ae2 --- /dev/null +++ b/contracts/docs/SonicCurveAMOStrategySquashed.svg @@ -0,0 +1,119 @@ + + + + + + +UmlClassDiagram + + + +410 + +SonicCurveAMOStrategy +../contracts/strategies/sonic/SonicCurveAMOStrategy.sol + +Private: +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   _deprecated_platformAddress: address <<InitializableAbstractStrategy>> +   _deprecated_vaultAddress: address <<InitializableAbstractStrategy>> +   _deprecated_rewardTokenAddress: address <<InitializableAbstractStrategy>> +   _deprecated_rewardLiquidationThreshold: uint256 <<InitializableAbstractStrategy>> +   _reserved: int256[98] <<InitializableAbstractStrategy>> +Internal: +   assetsMapped: address[] <<InitializableAbstractStrategy>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   platformAddress: address <<InitializableAbstractStrategy>> +   vaultAddress: address <<InitializableAbstractStrategy>> +   assetToPToken: mapping(address=>address) <<InitializableAbstractStrategy>> +   harvesterAddress: address <<InitializableAbstractStrategy>> +   rewardTokenAddresses: address[] <<InitializableAbstractStrategy>> +   SOLVENCY_THRESHOLD: uint256 <<BaseCurveAMOStrategy>> +   weth: IWETH9 <<BaseCurveAMOStrategy>> +   oeth: IERC20 <<BaseCurveAMOStrategy>> +   lpToken: IERC20 <<BaseCurveAMOStrategy>> +   curvePool: ICurveStableSwapNG <<BaseCurveAMOStrategy>> +   gauge: ICurveXChainLiquidityGauge <<BaseCurveAMOStrategy>> +   gaugeFactory: IChildLiquidityGaugeFactory <<BaseCurveAMOStrategy>> +   oethCoinIndex: uint128 <<BaseCurveAMOStrategy>> +   wethCoinIndex: uint128 <<BaseCurveAMOStrategy>> +   maxSlippage: uint256 <<BaseCurveAMOStrategy>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _initialize(_rewardTokenAddresses: address[], _assets: address[], _pTokens: address[]) <<InitializableAbstractStrategy>> +    _collectRewardTokens() <<InitializableAbstractStrategy>> +    _setPTokenAddress(_asset: address, _pToken: address) <<InitializableAbstractStrategy>> +    _abstractSetPToken(_asset: address, _pToken: address) <<BaseCurveAMOStrategy>> +    _deposit(_weth: address, _wethAmount: uint256) <<BaseCurveAMOStrategy>> +    calcTokenToBurn(_wethAmount: uint256): (lpToBurn: uint256) <<BaseCurveAMOStrategy>> +    _withdrawAndRemoveFromPool(_lpTokens: uint256, coinIndex: uint128): (coinsRemoved: uint256) <<BaseCurveAMOStrategy>> +    _solvencyAssert() <<BaseCurveAMOStrategy>> +    _lpWithdraw(_lpAmount: uint256) <<BaseCurveAMOStrategy>> +    _setMaxSlippage(_maxSlippage: uint256) <<BaseCurveAMOStrategy>> +    _approveBase() <<BaseCurveAMOStrategy>> +    _max(a: int256, b: int256): int256 <<BaseCurveAMOStrategy>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    collectRewardTokens() <<onlyHarvester, nonReentrant>> <<BaseCurveAMOStrategy>> +    setRewardTokenAddresses(_rewardTokenAddresses: address[]) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    getRewardTokenAddresses(): address[] <<InitializableAbstractStrategy>> +    setPTokenAddress(_asset: address, _pToken: address) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    removePToken(_assetIndex: uint256) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    setHarvesterAddress(_harvesterAddress: address) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    safeApproveAllTokens() <<onlyGovernor, nonReentrant>> <<BaseCurveAMOStrategy>> +    deposit(_weth: address, _amount: uint256) <<onlyVault, nonReentrant>> <<BaseCurveAMOStrategy>> +    depositAll() <<onlyVault, nonReentrant>> <<BaseCurveAMOStrategy>> +    withdraw(_recipient: address, _weth: address, _amount: uint256) <<onlyVault, nonReentrant>> <<BaseCurveAMOStrategy>> +    withdrawAll() <<onlyVaultOrGovernor, nonReentrant>> <<BaseCurveAMOStrategy>> +    checkBalance(_asset: address): (balance: uint256) <<BaseCurveAMOStrategy>> +    initialize(_rewardTokenAddresses: address[], _maxSlippage: uint256) <<onlyGovernor, initializer>> <<BaseCurveAMOStrategy>> +    mintAndAddOTokens(_oTokens: uint256) <<onlyStrategist, nonReentrant, improvePoolBalance>> <<BaseCurveAMOStrategy>> +    removeAndBurnOTokens(_lpTokens: uint256) <<onlyStrategist, nonReentrant, improvePoolBalance>> <<BaseCurveAMOStrategy>> +    removeOnlyAssets(_lpTokens: uint256) <<onlyStrategist, nonReentrant, improvePoolBalance>> <<BaseCurveAMOStrategy>> +    setMaxSlippage(_maxSlippage: uint256) <<onlyGovernor>> <<BaseCurveAMOStrategy>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> PTokenAdded(_asset: address, _pToken: address) <<InitializableAbstractStrategy>> +    <<event>> PTokenRemoved(_asset: address, _pToken: address) <<InitializableAbstractStrategy>> +    <<event>> Deposit(_asset: address, _pToken: address, _amount: uint256) <<InitializableAbstractStrategy>> +    <<event>> Withdrawal(_asset: address, _pToken: address, _amount: uint256) <<InitializableAbstractStrategy>> +    <<event>> RewardTokenCollected(recipient: address, rewardToken: address, amount: uint256) <<InitializableAbstractStrategy>> +    <<event>> RewardTokenAddressesUpdated(_oldAddresses: address[], _newAddresses: address[]) <<InitializableAbstractStrategy>> +    <<event>> HarvesterAddressesUpdated(_oldHarvesterAddress: address, _newHarvesterAddress: address) <<InitializableAbstractStrategy>> +    <<event>> MaxSlippageUpdated(newMaxSlippage: uint256) <<BaseCurveAMOStrategy>> +    <<modifier>> initializer() <<Initializable>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    <<modifier>> onlyVault() <<InitializableAbstractStrategy>> +    <<modifier>> onlyHarvester() <<InitializableAbstractStrategy>> +    <<modifier>> onlyVaultOrGovernor() <<InitializableAbstractStrategy>> +    <<modifier>> onlyVaultOrGovernorOrStrategist() <<InitializableAbstractStrategy>> +    <<modifier>> onlyStrategist() <<BaseCurveAMOStrategy>> +    <<modifier>> improvePoolBalance() <<BaseCurveAMOStrategy>> +    constructor() <<Governable>> +    governor(): address <<Governable>> +    isGovernor(): bool <<Governable>> +    constructor(_config: BaseStrategyConfig) <<InitializableAbstractStrategy>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    supportsAsset(_asset: address): bool <<BaseCurveAMOStrategy>> +    constructor(_baseConfig: BaseStrategyConfig, _os: address, _ws: address, _gauge: address, _gaugeFactory: address, _osCoinIndex: uint128, _wsCoinIndex: uint128) <<SonicCurveAMOStrategy>> + + + diff --git a/contracts/docs/SonicCurveAMOStrategyStorage.svg b/contracts/docs/SonicCurveAMOStrategyStorage.svg new file mode 100644 index 0000000000..ec832121c3 --- /dev/null +++ b/contracts/docs/SonicCurveAMOStrategyStorage.svg @@ -0,0 +1,129 @@ + + + + + + +StorageDiagram + + + +3 + +SonicCurveAMOStrategy <<Contract>> + +slot + +0 + +1-50 + +51 + +52 + +53 + +54 + +55 + +56 + +57 + +58 + +59-156 + +157 + +type: <inherited contract>.variable (bytes) + +unallocated (30) + +bool: Initializable.initializing (1) + +bool: Initializable.initialized (1) + +uint256[50]: Initializable.______gap (1600) + +unallocated (12) + +address: InitializableAbstractStrategy._deprecated_platformAddress (20) + +unallocated (12) + +address: InitializableAbstractStrategy._deprecated_vaultAddress (20) + +mapping(address=>address): InitializableAbstractStrategy.assetToPToken (32) + +address[]: InitializableAbstractStrategy.assetsMapped (32) + +unallocated (12) + +address: InitializableAbstractStrategy._deprecated_rewardTokenAddress (20) + +uint256: InitializableAbstractStrategy._deprecated_rewardLiquidationThreshold (32) + +unallocated (12) + +address: InitializableAbstractStrategy.harvesterAddress (20) + +address[]: InitializableAbstractStrategy.rewardTokenAddresses (32) + +int256[98]: InitializableAbstractStrategy._reserved (3136) + +uint256: BaseCurveAMOStrategy.maxSlippage (32) + + + +1 + +address[]: assetsMapped <<Array>> +0x4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b8 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +3:8->1 + + + + + +2 + +address[]: rewardTokenAddresses <<Array>> +0xa2999d817b6757290b50e8ecf3fa939673403dd35c97de392fdb343b4015ce9e + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +3:13->2 + + + + + diff --git a/contracts/docs/SonicSwapXAMOStrategyHierarchy.svg b/contracts/docs/SonicSwapXAMOStrategyHierarchy.svg new file mode 100644 index 0000000000..c6b6b8b24f --- /dev/null +++ b/contracts/docs/SonicSwapXAMOStrategyHierarchy.svg @@ -0,0 +1,61 @@ + + + + + + +UmlClassDiagram + + + +21 + +Governable +../contracts/governance/Governable.sol + + + +412 + +SonicSwapXAMOStrategy +../contracts/strategies/sonic/SonicSwapXAMOStrategy.sol + + + +290 + +<<Abstract>> +InitializableAbstractStrategy +../contracts/utils/InitializableAbstractStrategy.sol + + + +412->290 + + + + + +289 + +<<Abstract>> +Initializable +../contracts/utils/Initializable.sol + + + +290->21 + + + + + +290->289 + + + + + diff --git a/contracts/docs/SonicSwapXAMOStrategySquashed.svg b/contracts/docs/SonicSwapXAMOStrategySquashed.svg new file mode 100644 index 0000000000..0f57d114b3 --- /dev/null +++ b/contracts/docs/SonicSwapXAMOStrategySquashed.svg @@ -0,0 +1,117 @@ + + + + + + +UmlClassDiagram + + + +412 + +SonicSwapXAMOStrategy +../contracts/strategies/sonic/SonicSwapXAMOStrategy.sol + +Private: +   initialized: bool <<Initializable>> +   initializing: bool <<Initializable>> +   ______gap: uint256[50] <<Initializable>> +   governorPosition: bytes32 <<Governable>> +   pendingGovernorPosition: bytes32 <<Governable>> +   reentryStatusPosition: bytes32 <<Governable>> +   _deprecated_platformAddress: address <<InitializableAbstractStrategy>> +   _deprecated_vaultAddress: address <<InitializableAbstractStrategy>> +   _deprecated_rewardTokenAddress: address <<InitializableAbstractStrategy>> +   _deprecated_rewardLiquidationThreshold: uint256 <<InitializableAbstractStrategy>> +   _reserved: int256[98] <<InitializableAbstractStrategy>> +Internal: +   assetsMapped: address[] <<InitializableAbstractStrategy>> +Public: +   _NOT_ENTERED: uint256 <<Governable>> +   _ENTERED: uint256 <<Governable>> +   platformAddress: address <<InitializableAbstractStrategy>> +   vaultAddress: address <<InitializableAbstractStrategy>> +   assetToPToken: mapping(address=>address) <<InitializableAbstractStrategy>> +   harvesterAddress: address <<InitializableAbstractStrategy>> +   rewardTokenAddresses: address[] <<InitializableAbstractStrategy>> +   SOLVENCY_THRESHOLD: uint256 <<SonicSwapXAMOStrategy>> +   PRECISION: uint256 <<SonicSwapXAMOStrategy>> +   ws: address <<SonicSwapXAMOStrategy>> +   os: address <<SonicSwapXAMOStrategy>> +   pool: address <<SonicSwapXAMOStrategy>> +   gauge: address <<SonicSwapXAMOStrategy>> + +Internal: +    _governor(): (governorOut: address) <<Governable>> +    _pendingGovernor(): (pendingGovernor: address) <<Governable>> +    _setGovernor(newGovernor: address) <<Governable>> +    _setPendingGovernor(newGovernor: address) <<Governable>> +    _changeGovernor(_newGovernor: address) <<Governable>> +    _initialize(_rewardTokenAddresses: address[], _assets: address[], _pTokens: address[]) <<InitializableAbstractStrategy>> +    _collectRewardTokens() <<InitializableAbstractStrategy>> +    _setPTokenAddress(_asset: address, _pToken: address) <<InitializableAbstractStrategy>> +    _abstractSetPToken(_asset: address, _pToken: address) <<SonicSwapXAMOStrategy>> +    _deposit(_wS: address, _wsAmount: uint256) <<SonicSwapXAMOStrategy>> +    _calcTokensToMint(_wsAmount: uint256): (osAmount: uint256) <<SonicSwapXAMOStrategy>> +    _calcTokensToBurn(_wsAmount: uint256): (lpTokens: uint256) <<SonicSwapXAMOStrategy>> +    _depositToPoolAndGauge(_wsAmount: uint256, osAmount: uint256): (lpTokens: uint256) <<SonicSwapXAMOStrategy>> +    _withdrawFromGaugeAndPool(lpTokens: uint256) <<SonicSwapXAMOStrategy>> +    _swapExactTokensForTokens(_amountIn: uint256, _tokenIn: address, _tokenOut: address) <<SonicSwapXAMOStrategy>> +    _lpValue(lpTokens: uint256): (value: uint256) <<SonicSwapXAMOStrategy>> +    _invariant(x: uint256, y: uint256): (k: uint256) <<SonicSwapXAMOStrategy>> +    _solvencyAssert() <<SonicSwapXAMOStrategy>> +    _approveBase() <<SonicSwapXAMOStrategy>> +External: +    transferGovernance(_newGovernor: address) <<onlyGovernor>> <<Governable>> +    claimGovernance() <<Governable>> +    collectRewardTokens() <<onlyHarvester, nonReentrant>> <<SonicSwapXAMOStrategy>> +    setRewardTokenAddresses(_rewardTokenAddresses: address[]) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    getRewardTokenAddresses(): address[] <<InitializableAbstractStrategy>> +    setPTokenAddress(_asset: address, _pToken: address) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    removePToken(_assetIndex: uint256) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    setHarvesterAddress(_harvesterAddress: address) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    safeApproveAllTokens() <<onlyGovernor, nonReentrant>> <<SonicSwapXAMOStrategy>> +    deposit(_asset: address, _amount: uint256) <<onlyVault, nonReentrant, skimPool>> <<SonicSwapXAMOStrategy>> +    depositAll() <<onlyVault, nonReentrant, skimPool>> <<SonicSwapXAMOStrategy>> +    withdraw(_recipient: address, _asset: address, _wsAmount: uint256) <<onlyVault, nonReentrant, skimPool>> <<SonicSwapXAMOStrategy>> +    withdrawAll() <<onlyVaultOrGovernor, nonReentrant, skimPool>> <<SonicSwapXAMOStrategy>> +    initialize(_rewardTokenAddresses: address[]) <<onlyGovernor, initializer>> <<SonicSwapXAMOStrategy>> +    swapAssetsToPool(_wsAmount: uint256) <<onlyStrategist, nonReentrant, improvePoolBalance, skimPool>> <<SonicSwapXAMOStrategy>> +    swapOTokensToPool(_osAmount: uint256) <<onlyStrategist, nonReentrant, improvePoolBalance, skimPool>> <<SonicSwapXAMOStrategy>> +Public: +    <<event>> PendingGovernorshipTransfer(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> GovernorshipTransferred(previousGovernor: address, newGovernor: address) <<Governable>> +    <<event>> PTokenAdded(_asset: address, _pToken: address) <<InitializableAbstractStrategy>> +    <<event>> PTokenRemoved(_asset: address, _pToken: address) <<InitializableAbstractStrategy>> +    <<event>> Deposit(_asset: address, _pToken: address, _amount: uint256) <<InitializableAbstractStrategy>> +    <<event>> Withdrawal(_asset: address, _pToken: address, _amount: uint256) <<InitializableAbstractStrategy>> +    <<event>> RewardTokenCollected(recipient: address, rewardToken: address, amount: uint256) <<InitializableAbstractStrategy>> +    <<event>> RewardTokenAddressesUpdated(_oldAddresses: address[], _newAddresses: address[]) <<InitializableAbstractStrategy>> +    <<event>> HarvesterAddressesUpdated(_oldHarvesterAddress: address, _newHarvesterAddress: address) <<InitializableAbstractStrategy>> +    <<event>> SwapOTokensToPool(osMinted: uint256, wsLiquidity: uint256, osLiquidity: uint256, lpTokens: uint256) <<SonicSwapXAMOStrategy>> +    <<event>> SwapAssetsToPool(wsSwapped: uint256, lpTokens: uint256, osBurnt: uint256) <<SonicSwapXAMOStrategy>> +    <<modifier>> initializer() <<Initializable>> +    <<modifier>> onlyGovernor() <<Governable>> +    <<modifier>> nonReentrant() <<Governable>> +    <<modifier>> onlyVault() <<InitializableAbstractStrategy>> +    <<modifier>> onlyHarvester() <<InitializableAbstractStrategy>> +    <<modifier>> onlyVaultOrGovernor() <<InitializableAbstractStrategy>> +    <<modifier>> onlyVaultOrGovernorOrStrategist() <<InitializableAbstractStrategy>> +    <<modifier>> onlyStrategist() <<SonicSwapXAMOStrategy>> +    <<modifier>> skimPool() <<SonicSwapXAMOStrategy>> +    <<modifier>> improvePoolBalance() <<SonicSwapXAMOStrategy>> +    constructor() <<Governable>> +    governor(): address <<Governable>> +    isGovernor(): bool <<Governable>> +    constructor(_config: BaseStrategyConfig) <<InitializableAbstractStrategy>> +    transferToken(_asset: address, _amount: uint256) <<onlyGovernor>> <<InitializableAbstractStrategy>> +    checkBalance(_asset: address): (balance: uint256) <<SonicSwapXAMOStrategy>> +    supportsAsset(_asset: address): bool <<SonicSwapXAMOStrategy>> +    constructor(_baseConfig: BaseStrategyConfig, _os: address, _ws: address, _gauge: address) <<SonicSwapXAMOStrategy>> + + + diff --git a/contracts/docs/SonicSwapXAMOStrategyStorage.svg b/contracts/docs/SonicSwapXAMOStrategyStorage.svg new file mode 100644 index 0000000000..a1529c3b6a --- /dev/null +++ b/contracts/docs/SonicSwapXAMOStrategyStorage.svg @@ -0,0 +1,125 @@ + + + + + + +StorageDiagram + + + +3 + +SonicSwapXAMOStrategy <<Contract>> + +slot + +0 + +1-50 + +51 + +52 + +53 + +54 + +55 + +56 + +57 + +58 + +59-156 + +type: <inherited contract>.variable (bytes) + +unallocated (30) + +bool: Initializable.initializing (1) + +bool: Initializable.initialized (1) + +uint256[50]: Initializable.______gap (1600) + +unallocated (12) + +address: InitializableAbstractStrategy._deprecated_platformAddress (20) + +unallocated (12) + +address: InitializableAbstractStrategy._deprecated_vaultAddress (20) + +mapping(address=>address): InitializableAbstractStrategy.assetToPToken (32) + +address[]: InitializableAbstractStrategy.assetsMapped (32) + +unallocated (12) + +address: InitializableAbstractStrategy._deprecated_rewardTokenAddress (20) + +uint256: InitializableAbstractStrategy._deprecated_rewardLiquidationThreshold (32) + +unallocated (12) + +address: InitializableAbstractStrategy.harvesterAddress (20) + +address[]: InitializableAbstractStrategy.rewardTokenAddresses (32) + +int256[98]: InitializableAbstractStrategy._reserved (3136) + + + +1 + +address[]: assetsMapped <<Array>> +0x4a11f94e20a93c79f6ec743a1954ec4fc2c08429ae2122118bf234b2185c81b8 + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +3:8->1 + + + + + +2 + +address[]: rewardTokenAddresses <<Array>> +0xa2999d817b6757290b50e8ecf3fa939673403dd35c97de392fdb343b4015ce9e + +offset + +0 + +type: variable (bytes) + +unallocated (12) + +address (20) + + + +3:13->2 + + + + + diff --git a/contracts/docs/VaultHierarchy.svg b/contracts/docs/VaultHierarchy.svg index 044158df15..2954412d6e 100644 --- a/contracts/docs/VaultHierarchy.svg +++ b/contracts/docs/VaultHierarchy.svg @@ -4,128 +4,180 @@ - - + + UmlClassDiagram - - + + -20 - -Governable -../contracts/governance/Governable.sol +21 + +Governable +../contracts/governance/Governable.sol - + -215 - -OUSD -../contracts/token/OUSD.sol +261 + +OUSD +../contracts/token/OUSD.sol - - -215->20 - - + + +261->21 + + - + -225 - -<<Abstract>> -Initializable -../contracts/utils/Initializable.sol - - - -215->225 - - +277 + +<<Abstract>> +Initializable +../contracts/utils/Initializable.sol - + -228 - -<<Abstract>> -InitializableERC20Detailed -../contracts/utils/InitializableERC20Detailed.sol +282 + +OETHBaseVaultAdmin +../contracts/vault/OETHBaseVaultAdmin.sol + + + +286 + +OETHVaultAdmin +../contracts/vault/OETHVaultAdmin.sol - + -215->228 - - +282->286 + + - + -237 - -VaultAdmin -../contracts/vault/VaultAdmin.sol +283 + +OETHBaseVaultCore +../contracts/vault/OETHBaseVaultCore.sol - - -240 - -VaultStorage -../contracts/vault/VaultStorage.sol - - - -237->240 - - - - - -238 - -VaultCore -../contracts/vault/VaultCore.sol - - + -239 - -VaultInitializer -../contracts/vault/VaultInitializer.sol +287 + +OETHVaultCore +../contracts/vault/OETHVaultCore.sol - + + +283->287 + + + + + +293 + +VaultAdmin +../contracts/vault/VaultAdmin.sol + + + +286->293 + + + + + +294 + +VaultCore +../contracts/vault/VaultCore.sol + + -238->239 - - +287->294 + + - - -239->215 - - + + +289 + +OSonicVaultAdmin +../contracts/vault/OSonicVaultAdmin.sol - + -239->240 - - - - +289->286 + + + + + +290 + +OSonicVaultCore +../contracts/vault/OSonicVaultCore.sol + + + +290->287 + + + + + +296 + +VaultStorage +../contracts/vault/VaultStorage.sol + + + +293->296 + + + + + +295 + +VaultInitializer +../contracts/vault/VaultInitializer.sol + + -240->20 - - +294->295 + + - + -240->215 - - - - - -240->225 - - +295->296 + + + + + +296->21 + + + + + +296->261 + + + + + +296->277 + + diff --git a/contracts/docs/generate.sh b/contracts/docs/generate.sh index 25131def26..e2d56117ac 100644 --- a/contracts/docs/generate.sh +++ b/contracts/docs/generate.sh @@ -13,21 +13,29 @@ sol2uml .. -s -d 0 -b Flipper -o FlipperSquashed.svg sol2uml storage .. -c Flipper -o FlipperStorage.svg # contracts/harvest -sol2uml .. -v -hv -hf -he -hs -hl -b Dripper -o DripperHierarchy.svg +sol2uml .. -v -hv -hf -he -hs -hl -hi -b Dripper -o DripperHierarchy.svg sol2uml .. -s -d 0 -b Dripper -o DripperSquashed.svg sol2uml storage .. -c Dripper -o DripperStorage.svg -sol2uml .. -v -hv -hf -he -hs -hl -b OETHDripper -o OETHDripperHierarchy.svg -sol2uml .. -s -d 0 -b OETHDripper -o OETHDripperSquashed.svg -sol2uml storage .. -c OETHDripper -o OETHDripperStorage.svg +sol2uml .. -v -hv -hf -he -hs -hl -hi -b OETHFixedRateDripper -o OETHFixedRateDripperHierarchy.svg +sol2uml .. -s -d 0 -b OETHFixedRateDripper -o OETHFixedRateDripperSquashed.svg +sol2uml storage .. -c OETHFixedRateDripper -o OETHFixedRateDripperStorage.svg -sol2uml .. -v -hv -hf -he -hs -hl -b Harvester -o HarvesterHierarchy.svg +sol2uml .. -v -hv -hf -he -hs -hl -hi -b Harvester -o HarvesterHierarchy.svg sol2uml .. -s -d 0 -b Harvester -o HarvesterSquashed.svg sol2uml storage .. -c Harvester -o HarvesterStorage.svg -sol2uml .. -v -hv -hf -he -hs -hl -b OETHHarvester -o OETHHarvesterHierarchy.svg -sol2uml .. -s -d 0 -b OETHHarvester -o OETHHarvesterSquashed.svg -sol2uml storage .. -c OETHHarvester -o OETHHarvesterStorage.svg +sol2uml .. -v -hv -hf -he -hs -hl -hi -b OETHHarvesterSimple -o OETHHarvesterSimpleHierarchy.svg +sol2uml .. -s -d 0 -b OETHHarvesterSimple -o OETHHarvesterSimpleSquashed.svg +sol2uml storage .. -c OETHHarvesterSimple -o OETHHarvesterSimpleStorage.svg --hideExpand __gap,___gap,______gap + +sol2uml .. -v -hv -hf -he -hs -hl -hi -b OETHBaseHarvester -o OETHBaseHarvesterHierarchy.svg +sol2uml .. -s -d 0 -b OETHBaseHarvester -o OETHBaseHarvesterSquashed.svg +sol2uml storage .. -c OETHBaseHarvester -o OETHBaseHarvesterStorage.svg --hideExpand __gap,___gap,______gap + +sol2uml .. -v -hv -hf -he -hs -hl -hi -b OSonicHarvester -o OSonicHarvesterHierarchy.svg +sol2uml .. -s -d 0 -b OSonicHarvester -o OSonicHarvesterSquashed.svg +sol2uml storage .. -c OSonicHarvester -o OSonicHarvesterStorage.svg --hideExpand __gap,___gap,______gap # contracts/governance sol2uml .. -v -hv -hf -he -hs -hl -b Governor -o GovernorHierarchy.svg @@ -110,6 +118,14 @@ sol2uml .. -v -hv -hf -he -hs -hl -hi -b SonicStakingStrategy -o SonicStakingStr sol2uml .. -s -d 0 -b SonicStakingStrategy -o SonicStakingStrategySquashed.svg sol2uml storage .. -c SonicStakingStrategy -o SonicStakingStrategyStorage.svg --hideExpand __gap,______gap,_reserved +sol2uml .. -v -hv -hf -he -hs -hl -hi -b SonicCurveAMOStrategy -o SonicCurveAMOStrategyHierarchy.svg +sol2uml .. -s -d 0 -b SonicCurveAMOStrategy -o SonicCurveAMOStrategySquashed.svg +sol2uml storage .. -c SonicCurveAMOStrategy -o SonicCurveAMOStrategyStorage.svg --hideExpand __gap,______gap,_reserved + +sol2uml .. -v -hv -hf -he -hs -hl -hi -b SonicSwapXAMOStrategy -o SonicSwapXAMOStrategyHierarchy.svg +sol2uml .. -s -d 0 -b SonicSwapXAMOStrategy -o SonicSwapXAMOStrategySquashed.svg +sol2uml storage .. -c SonicSwapXAMOStrategy -o SonicSwapXAMOStrategyStorage.svg --hideExpand __gap,______gap,_reserved + # contracts/swapper sol2uml .. -v -hv -hf -he -hs -hl -b Swapper1InchV5 -o Swapper1InchV5Hierarchy.svg sol2uml .. -s -d 0 -b Swapper1InchV5 -o Swapper1InchV5Squashed.svg @@ -156,17 +172,21 @@ sol2uml .. -s -d 0 -b WOSonic -o WOSonicSquashed.svg sol2uml storage .. -c WOSonic -o WOSonicStorage.svg --hideExpand ______gap # contracts/vault -sol2uml .. -v -hv -hf -he -hs -hl -hi -b VaultCore,VaultAdmin -o VaultHierarchy.svg + +sol2uml .. -v -hv -hf -he -hs -hl -hi -b OETHBaseVaultCore,OETHBaseVaultAdmin,OSonicVaultCore,OSonicVaultAdmin -o VaultHierarchy.svg + sol2uml .. -s -d 0 -b VaultCore -o VaultCoreSquashed.svg sol2uml .. -s -d 0 -b VaultAdmin -o VaultAdminSquashed.svg sol2uml storage .. -c VaultCore -o VaultStorage.svg --hideExpand __gap,______gap,_deprecated_swapTokens -sol2uml .. -v -hv -hf -he -hs -hl -hi -b OETHVaultCore,OETHVaultAdmin -o OETHVaultHierarchy.svg sol2uml .. -s -d 0 -b OETHVaultCore -o OETHVaultCoreSquashed.svg sol2uml .. -s -d 0 -b OETHVaultAdmin -o OETHVaultAdminSquashed.svg sol2uml storage .. -c OETHVaultCore -o OETHVaultStorage.svg --hideExpand __gap,______gap,_deprecated_swapTokens -sol2uml .. -v -hv -hf -he -hs -hl -hi -b OSonicVaultCore,OSonicVaultAdmin -o OSonicVaultHierarchy.svg +sol2uml .. -s -d 0 -b OETHBaseVaultCore -o OETHBaseVaultCoreSquashed.svg +sol2uml .. -s -d 0 -b OETHBaseVaultAdmin -o OETHBaseVaultAdminSquashed.svg +sol2uml storage .. -c OETHBaseVaultCore -o OETHBaseVaultStorage.svg --hideExpand __gap,______gap + sol2uml .. -s -d 0 -b OSonicVaultCore -o OSonicVaultCoreSquashed.svg sol2uml .. -s -d 0 -b OSonicVaultAdmin -o OSonicVaultAdminSquashed.svg sol2uml storage .. -c OSonicVaultCore -o OSonicVaultStorage.svg --hideExpand __gap,______gap,_deprecated_swapTokens diff --git a/contracts/docs/plantuml/sonicContracts.png b/contracts/docs/plantuml/sonicContracts.png index 913c2b9dca..03924d20e3 100644 Binary files a/contracts/docs/plantuml/sonicContracts.png and b/contracts/docs/plantuml/sonicContracts.png differ diff --git a/contracts/docs/plantuml/sonicContracts.puml b/contracts/docs/plantuml/sonicContracts.puml index 2a54a3769b..192b3523d4 100644 --- a/contracts/docs/plantuml/sonicContracts.puml +++ b/contracts/docs/plantuml/sonicContracts.puml @@ -9,9 +9,9 @@ legend blue - Origin -' green - new +green - new ' orange - changed -yellow - phase2 +' yellow - phase2 white - 3rd Party end legend @@ -43,6 +43,14 @@ object "OSonicVault" as vault <><> #$originColor { asset: wS } +object "OSonicHarvester" as harv <><> #$originColor { + rewards: CRV, SWPx +} + +' Oracle +object "OSonicOracleRouter" as router <> #DeepSkyBlue { +} + object "SonicStakingStrategy" as stakeStrat <><> #$originColor { asset: wS } @@ -51,17 +59,34 @@ object "Special Fee Contract" as sfc <><> { asset: S } -object "AMOStrategy" as amoStrat <><> #$phase2 { +object "Curve AMO Strategy" as curveAmoStrat <><> #$originColor { asset: wS - reward: ? + reward: CRV } -object "OSonicHarvester" as harv <><> #$phase2 { - rewards: ? +object "Curve OS/wS\nPool" as curvePool <> { + assets: OS, wS + lp: OSwS } -' Oracle -object "OSonicOracleRouter" as router <> #DeepSkyBlue { +object "Curve OS/wS\nGauge" as curveGauge <> { + asset: OSwS + lp: OSwS-gauge +} + +object "SwapX AMO Strategy" as swapXAmoStrat <><> #$originColor { + asset: wS + reward: SWPx +} + +object "SwapX OS/wS\nPool" as swapXPool <> { + assets: OS, wS + lp: OSwS +} + +object "SwapX OS/wS\nGauge" as swapXGauge <> { + asset: OSwS + lp: OSwS-gauge } wos <. zap @@ -75,13 +100,18 @@ os <.> vault vault <.> drip vault <...> stakeStrat stakeStrat ..> sfc -vault <...> amoStrat +vault <...> curveAmoStrat vault .> router vault <.. harv drip <.. harv -harv <..> amoStrat +harv <..> curveAmoStrat +curveAmoStrat ..> curvePool +curveAmoStrat ..> curveGauge +harv <..> swapXAmoStrat +swapXAmoStrat ..> swapXPool +swapXAmoStrat ..> swapXGauge @enduml \ No newline at end of file diff --git a/contracts/tasks/amoStrategy.js b/contracts/tasks/amoStrategy.js index d9ca81df81..9a434239a0 100644 --- a/contracts/tasks/amoStrategy.js +++ b/contracts/tasks/amoStrategy.js @@ -18,7 +18,7 @@ const { logTxDetails } = require("../utils/txLogger"); const log = require("../utils/logger")("task:curve"); /** - * hardhat task that dumps the current state of a AMO Strategy + * Hardhat task that dumps the current state of an AMO strategy */ async function amoStrategyTask(taskArguments) { const poolOTokenSymbol = taskArguments.pool; diff --git a/contracts/tasks/tasks.js b/contracts/tasks/tasks.js index 58bcfbdbcd..1f7718665a 100644 --- a/contracts/tasks/tasks.js +++ b/contracts/tasks/tasks.js @@ -705,7 +705,7 @@ task("curvePool").setAction(async (_, __, runSuper) => { }); // Curve Pools -subtask("amoStrat", "Dumps the current state of a AMO strategy") +subtask("amoStrat", "Dumps the current state of an AMO strategy") .addParam("pool", "Symbol of the curve Metapool. OUSD or OETH") .addOptionalParam( "block", @@ -725,6 +725,12 @@ subtask("amoStrat", "Dumps the current state of a AMO strategy") true, types.boolean ) + .addOptionalParam( + "amm", + "Type of pool. eg curve, balancer or swapx", + "curve", + types.string + ) .setAction(amoStrategyTask); task("amoStrat").setAction(async (_, __, runSuper) => { return runSuper(); diff --git a/contracts/test/_fixture-sonic.js b/contracts/test/_fixture-sonic.js index db11cb13a1..316bbe1afb 100644 --- a/contracts/test/_fixture-sonic.js +++ b/contracts/test/_fixture-sonic.js @@ -1,12 +1,19 @@ const hre = require("hardhat"); const { ethers } = hre; +const { parseUnits } = ethers.utils; const mocha = require("mocha"); +const hhHelpers = require("@nomicfoundation/hardhat-network-helpers"); + const { isFork, isSonicFork, oethUnits } = require("./helpers"); const { deployWithConfirmation } = require("../utils/deploy.js"); const { impersonateAndFund } = require("../utils/signers"); const { nodeRevert, nodeSnapshot } = require("./_fixture"); const addresses = require("../utils/addresses"); -const hhHelpers = require("@nomicfoundation/hardhat-network-helpers"); + +const erc20Abi = require("./abi/erc20.json"); +const curveXChainLiquidityGaugeAbi = require("./abi/curveXChainLiquidityGauge.json"); +const curveStableSwapNGAbi = require("./abi/curveStableSwapNG.json"); +const curveChildLiquidityGaugeFactoryAbi = require("./abi/curveChildLiquidityGaugeFactory.json"); const log = require("../utils/logger")("test:fixtures-sonic"); @@ -92,12 +99,20 @@ const defaultSonicFixture = deployments.createFixture(async () => { const sfc = await ethers.getContractAt("ISFC", addresses.sonic.SFC); - let dripper, + let harvester, + dripper, zapper, poolBoosterDoubleFactoryV1, poolBoosterSingleFactoryV1, poolBoosterCentralRegistry; if (isFork) { + // Harvester + const harvesterProxy = await ethers.getContract("OSonicHarvesterProxy"); + harvester = await ethers.getContractAt( + "OSonicHarvester", + harvesterProxy.address + ); + // Dripper const dripperProxy = await ethers.getContract("OSonicDripperProxy"); dripper = await ethers.getContractAt( @@ -137,7 +152,12 @@ const defaultSonicFixture = deployments.createFixture(async () => { const [minter, burner, rafael, nick, clement] = signers.slice(4); // Skip first 4 addresses to avoid conflict - let validatorRegistrator; + let validatorRegistrator, + curveAMOStrategy, + curvePool, + curveGauge, + curveChildLiquidityGaugeFactory, + crv; if (isFork) { validatorRegistrator = await impersonateAndFund( addresses.sonic.validatorRegistrator @@ -145,6 +165,32 @@ const defaultSonicFixture = deployments.createFixture(async () => { validatorRegistrator.address = addresses.sonic.validatorRegistrator; await sonicStakingStrategy.connect(strategist).setDefaultValidatorId(18); + + // Curve AMO + const curveAMOProxy = await ethers.getContract( + "SonicCurveAMOStrategyProxy" + ); + curveAMOStrategy = await ethers.getContractAt( + "SonicCurveAMOStrategy", + curveAMOProxy.address + ); + + curvePool = await ethers.getContractAt( + curveStableSwapNGAbi, + addresses.sonic.WS_OS.pool + ); + + curveGauge = await ethers.getContractAt( + curveXChainLiquidityGaugeAbi, + addresses.sonic.WS_OS.gauge + ); + + curveChildLiquidityGaugeFactory = await ethers.getContractAt( + curveChildLiquidityGaugeFactoryAbi, + addresses.sonic.childLiquidityGaugeFactory + ); + + crv = await ethers.getContractAt(erc20Abi, addresses.sonic.CRV); } for (const user of [rafael, nick, clement]) { @@ -153,7 +199,7 @@ const defaultSonicFixture = deployments.createFixture(async () => { await wS.connect(user).deposit({ value: oethUnits("10000000") }); // Set allowance on the vault - await wS.connect(user).approve(oSonicVault.address, oethUnits("5000000")); + await wS.connect(user).approve(oSonicVault.address, oethUnits("100000000")); } return { @@ -161,8 +207,7 @@ const defaultSonicFixture = deployments.createFixture(async () => { oSonic, oSonicVault, wOSonic, - // harvester, - // dripper, + harvester, sonicStakingStrategy, dripper, zapper, @@ -173,6 +218,13 @@ const defaultSonicFixture = deployments.createFixture(async () => { // Wrapped S wS, + // Curve + curveAMOStrategy, + curvePool, + curveGauge, + curveChildLiquidityGaugeFactory, + crv, + // Signers governor, strategist, @@ -191,6 +243,122 @@ const defaultSonicFixture = deployments.createFixture(async () => { }; }); +/** + * Configure a Vault with only the OETH/(W)ETH Curve Metastrategy. + */ +async function swapXAMOFixture( + config = { + wsMintAmount: 0, + depositToStrategy: false, + balancePool: false, + poolAddwSAmount: 0, + poolAddOSAmount: 0, + } +) { + const fixture = await defaultSonicFixture(); + + const { oSonic, oSonicVault, rafael, nick, strategist, timelock, wS } = + fixture; + + let swapXAMOStrategy, swapXPool, swapXGauge; + + if (isFork) { + const swapXAMOProxy = await ethers.getContract( + "SonicSwapXAMOStrategyProxy" + ); + swapXAMOStrategy = await ethers.getContractAt( + "SonicSwapXAMOStrategy", + swapXAMOProxy.address + ); + + swapXPool = await ethers.getContractAt( + "IPair", + addresses.sonic.SwapXWSOS.pool + ); + + swapXGauge = await ethers.getContractAt( + "IGauge", + addresses.sonic.SwapXWSOS.gauge + ); + } + + await oSonicVault + .connect(timelock) + .setAssetDefaultStrategy(wS.address, addresses.zero); + + // mint some OS using wS if configured + if (config?.wsMintAmount > 0) { + const wsAmount = parseUnits(config.wsMintAmount.toString()); + await oSonicVault.connect(nick).rebase(); + await oSonicVault.connect(nick).allocate(); + + // Calculate how much to mint based on the wS in the vault, + // the withdrawal queue, and the wS to be sent to the strategy + const wsBalance = await wS.balanceOf(oSonicVault.address); + const queue = await oSonicVault.withdrawalQueueMetadata(); + const available = wsBalance.add(queue.claimed).sub(queue.queued); + const mintAmount = wsAmount.sub(available); + + if (mintAmount.gt(0)) { + // Approve the Vault to transfer wS + await wS.connect(nick).approve(oSonicVault.address, mintAmount); + + // Mint OS with wS + // This will sit in the vault, not the strategy + await oSonicVault.connect(nick).mint(wS.address, mintAmount, 0); + } + + // Add ETH to the Metapool + if (config?.depositToStrategy) { + // The strategist deposits the WETH to the AMO strategy + await oSonicVault + .connect(strategist) + .depositToStrategy(swapXAMOStrategy.address, [wS.address], [wsAmount]); + } + } + + if (config?.balancePool) { + const { _reserve0: wsReserves, _reserve1: osReserves } = + await swapXPool.getReserves(); + + const diff = parseInt( + wsReserves.sub(osReserves).div(oethUnits("1")).toString() + ); + + if (diff > 0) { + config.poolAddOSAmount = (config.poolAddOSAmount || 0) + diff; + } else if (diff < 0) { + config.poolAddwSAmount = (config.poolAddwSAmount || 0) - diff; + } + } + + // Add wS to the pool + if (config?.poolAddwSAmount > 0) { + log(`Adding ${config.poolAddwSAmount} wS to the pool`); + // transfer wS to the pool + const wsAmount = parseUnits(config.poolAddwSAmount.toString(), 18); + await wS.connect(nick).transfer(swapXPool.address, wsAmount); + } + + // Add OS to the pool + if (config?.poolAddOSAmount > 0) { + log(`Adding ${config.poolAddOSAmount} OS to the pool`); + + const osAmount = parseUnits(config.poolAddOSAmount.toString(), 18); + + // Mint OS with wS + await oSonicVault.connect(rafael).mint(wS.address, osAmount, 0); + + // transfer OS to the pool + await oSonic.connect(rafael).transfer(swapXPool.address, osAmount); + } + + // force reserves to match balances + await swapXPool.sync(); + + return { ...fixture, swapXAMOStrategy, swapXPool, swapXGauge }; +} + const deployPoolBoosterFactorySwapxSingle = async ( poolBoosterCentralRegistry, governor @@ -225,6 +393,7 @@ mocha.after(async () => { module.exports = { defaultSonicFixture, + swapXAMOFixture, MINTER_ROLE, BURNER_ROLE, }; diff --git a/contracts/test/_fund.js b/contracts/test/_fund.js index bc5680dbe0..5b0adc7b27 100644 --- a/contracts/test/_fund.js +++ b/contracts/test/_fund.js @@ -44,9 +44,9 @@ const balancesContractSlotCache = { const findBalancesSlot = async (tokenAddress) => { tokenAddress = tokenAddress.toLowerCase(); if (balancesContractSlotCache[tokenAddress]) { - // console.log( - // `Found balance slot ${balancesContractSlotCache[tokenAddress]} for ${tokenAddress} in cache` - // ); + log( + `Found balance slot ${balancesContractSlotCache[tokenAddress]} for ${tokenAddress} in cache` + ); return balancesContractSlotCache[tokenAddress]; } diff --git a/contracts/test/behaviour/strategy.js b/contracts/test/behaviour/strategy.js index 38dbc7659b..a8dd60e82f 100644 --- a/contracts/test/behaviour/strategy.js +++ b/contracts/test/behaviour/strategy.js @@ -15,6 +15,7 @@ const { parseUnits } = require("ethers/lib/utils"); * - valueAssets: optional array of assets that work for checkBalance and withdraw. defaults to assets * - vault: Vault or OETHVault contract * - harvester: Harvester or OETHHarvester contract + * - checkWithdrawAmounts: Check the amounts in the withdrawAll events. defaults to true * @example shouldBehaveLikeStrategy(() => ({ ...fixture, @@ -23,6 +24,7 @@ const { parseUnits } = require("ethers/lib/utils"); valueAssets: [fixture.frxETH], harvester: fixture.oethHarvester, vault: fixture.oethVault, + checkWithdrawAmounts: true, })); */ const shouldBehaveLikeStrategy = (context) => { @@ -72,6 +74,11 @@ const shouldBehaveLikeStrategy = (context) => { } }); describe("with no assets in the strategy", () => { + beforeEach(async () => { + const { strategy, governor } = context(); + + await strategy.connect(governor).withdrawAll(); + }); it("Should check asset balances", async () => { const { assets, josh, strategy } = context(); @@ -213,12 +220,7 @@ const shouldBehaveLikeStrategy = (context) => { } }); it("Should be able to call withdraw all by vault", async () => { - const { strategy, vault, curveAMOStrategy } = await context(); - - // If strategy is Curve Base AMO, withdrawAll cannot work if there are no assets in the strategy. - // As it will try to remove 0 LPs from the gauge, which is not permitted by Curve gauge. - if (curveAMOStrategy != undefined && curveAMOStrategy == strategy) - return; + const { strategy, vault } = await context(); const vaultSigner = await impersonateAndFund(vault.address); @@ -227,12 +229,8 @@ const shouldBehaveLikeStrategy = (context) => { await expect(tx).to.not.emit(strategy, "Withdrawal"); }); it("Should be able to call withdraw all by governor", async () => { - const { strategy, governor, curveAMOStrategy } = await context(); + const { strategy, governor } = await context(); - // If strategy is Curve Base AMO, withdrawAll cannot work if there are no assets in the strategy. - // As it will try to remove 0 LPs from the gauge, which is not permitted by Curve gauge. - if (curveAMOStrategy != undefined && curveAMOStrategy == strategy) - return; const tx = await strategy.connect(governor).withdrawAll(); await expect(tx).to.not.emit(strategy, "Withdrawal"); @@ -318,7 +316,7 @@ const shouldBehaveLikeStrategy = (context) => { vault, fraxEthStrategy, sfrxETH, - curveAMOStrategy, + checkWithdrawAmounts, } = await context(); const vaultSigner = await impersonateAndFund(vault.address); @@ -341,12 +339,23 @@ const shouldBehaveLikeStrategy = (context) => { withdrawAmount.mul(3) ); } else if ( - curveAMOStrategy != undefined && - curveAMOStrategy == strategy + checkWithdrawAmounts != undefined && + checkWithdrawAmounts == false ) { - // Didn't managed to get this work with args. - await expect(tx).to.emit(strategy, "Withdrawal"); - await expect(tx).to.emit(asset, "Transfer"); + await expect(tx).to.emit(strategy, "Withdrawal").withNamedArgs({ + _asset: asset.address, + _pToken: platformAddress, + }); + + // the transfer does not have to come from the strategy. It can come directly from the platform + // Need to handle WETH which has different named args in the Transfer event + const erc20Asset = await ethers.getContractAt( + "IERC20", + asset.address + ); + await expect(tx) + .to.emit(erc20Asset, "Transfer") + .withNamedArgs({ from: strategy.address, to: vault.address }); } else { await expect(tx) .to.emit(strategy, "Withdrawal") diff --git a/contracts/test/helpers.js b/contracts/test/helpers.js index 59125f272b..404027db79 100644 --- a/contracts/test/helpers.js +++ b/contracts/test/helpers.js @@ -12,7 +12,6 @@ const { decimalsFor, units } = require("../utils/units"); */ chai.Assertion.addMethod("withinRange", function (min, max, message) { const actual = this._obj; - min = BigNumber.from(min); chai.expect(actual, message).gte(min); chai.expect(actual, message).lte(max); diff --git a/contracts/test/strategies/base/aerodrome-amo.base.fork-test.js b/contracts/test/strategies/base/aerodrome-amo.base.fork-test.js index 0888bb6969..15a79c2753 100644 --- a/contracts/test/strategies/base/aerodrome-amo.base.fork-test.js +++ b/contracts/test/strategies/base/aerodrome-amo.base.fork-test.js @@ -18,7 +18,7 @@ const baseFixture = createFixtureLoader(defaultBaseFixture); const { setERC20TokenBalance } = require("../../_fund"); const futureEpoch = 1924064072; -describe("ForkTest: Aerodrome AMO Strategy empty pool setup (Base)", async function () { +describe("Base Fork Test: Aerodrome AMO Strategy empty pool setup (Base)", async function () { let fixture, oethbVault, oethb, diff --git a/contracts/test/strategies/base/bridged-woeth-strategy.base.fork-test.js b/contracts/test/strategies/base/bridged-woeth-strategy.base.fork-test.js index 7120b7a009..246d0f09ca 100644 --- a/contracts/test/strategies/base/bridged-woeth-strategy.base.fork-test.js +++ b/contracts/test/strategies/base/bridged-woeth-strategy.base.fork-test.js @@ -8,7 +8,7 @@ const addresses = require("../../../utils/addresses"); const baseFixture = createFixtureLoader(defaultBaseFixture); -describe("ForkTest: Bridged WOETH Strategy", function () { +describe("Base Fork Test: Bridged WOETH Strategy", function () { let fixture; beforeEach(async () => { fixture = await baseFixture(); diff --git a/contracts/test/strategies/base/bridged-woeth-strategy.base.js b/contracts/test/strategies/base/bridged-woeth-strategy.base.js index feae40e1f0..c823191580 100644 --- a/contracts/test/strategies/base/bridged-woeth-strategy.base.js +++ b/contracts/test/strategies/base/bridged-woeth-strategy.base.js @@ -5,7 +5,7 @@ const { oethUnits } = require("../../helpers"); const baseFixture = createFixtureLoader(defaultBaseFixture); -describe("Bridged WOETH Strategy", function () { +describe("Base Fork Test: Bridged WOETH Strategy", function () { let fixture; beforeEach(async () => { fixture = await baseFixture(); diff --git a/contracts/test/strategies/base/curve-amo.base.fork-test.js b/contracts/test/strategies/base/curve-amo.base.fork-test.js index c1209b2fe2..0ebff91546 100644 --- a/contracts/test/strategies/base/curve-amo.base.fork-test.js +++ b/contracts/test/strategies/base/curve-amo.base.fork-test.js @@ -1,19 +1,23 @@ +const { expect } = require("chai"); +const { formatUnits } = require("ethers/lib/utils"); +const hre = require("hardhat"); + const { createFixtureLoader } = require("../../_fixture"); const { defaultBaseFixture } = require("../../_fixture-base"); -const { expect } = require("chai"); const { oethUnits } = require("../../helpers"); const addresses = require("../../../utils/addresses"); const { impersonateAndFund } = require("../../../utils/signers"); const { setERC20TokenBalance } = require("../../_fund"); -const hre = require("hardhat"); const { advanceTime } = require("../../helpers"); const { shouldBehaveLikeGovernable } = require("../../behaviour/governable"); const { shouldBehaveLikeHarvestable } = require("../../behaviour/harvestable"); const { shouldBehaveLikeStrategy } = require("../../behaviour/strategy"); +const log = require("../../../utils/logger")("test:fork:sonic:curve-amo"); + const baseFixture = createFixtureLoader(defaultBaseFixture); -describe("Curve AMO strategy", function () { +describe("Base Fork Test: Curve AMO strategy", function () { let fixture, oethbVault, curveAMOStrategy, @@ -391,36 +395,65 @@ describe("Curve AMO strategy", function () { }); it("Should deposit when pool is heavily unbalanced with OETH", async () => { - await balancePool(); - await mintAndDepositToStrategy(); + await unbalancePool({ oethbAmount: defaultDeposit.mul(20) }); - await unbalancePool({ oethbAmount: defaultDeposit.mul(1000) }); + const checkBalanceBefore = await curveAMOStrategy.checkBalance( + weth.address + ); + log(`AMO checkBalance before deposit ${formatUnits(checkBalanceBefore)}`); + const gaugeTokensBefore = await curveGauge.balanceOf( + curveAMOStrategy.address + ); - await curveAMOStrategy.connect(impersonatedVaultSigner).depositAll(); + await mintAndDepositToStrategy(); + + const checkBalanceAfter = await curveAMOStrategy.checkBalance( + weth.address + ); + log(`AMO checkBalance after deposit ${formatUnits(checkBalanceAfter)}`); + log( + `AMO checkBalance diff ${formatUnits( + checkBalanceAfter.sub(checkBalanceBefore) + )}` + ); expect( await curveAMOStrategy.checkBalance(weth.address) - ).to.approxEqualTolerance(defaultDeposit.mul(2)); + ).to.approxEqualTolerance(defaultDeposit.mul(2).add(checkBalanceBefore)); expect( await curveGauge.balanceOf(curveAMOStrategy.address) - ).to.approxEqualTolerance(defaultDeposit.mul(2)); + ).to.approxEqualTolerance(defaultDeposit.mul(2).add(gaugeTokensBefore)); expect(await weth.balanceOf(curveAMOStrategy.address)).to.equal(0); }); it("Should deposit when pool is heavily unbalanced with WETH", async () => { - await balancePool(); - await mintAndDepositToStrategy(); + await unbalancePool({ wethbAmount: defaultDeposit.mul(20) }); - await unbalancePool({ wethbAmount: defaultDeposit.mul(1000) }); + const checkBalanceBefore = await curveAMOStrategy.checkBalance( + weth.address + ); + const gaugeTokensBefore = await curveGauge.balanceOf( + curveAMOStrategy.address + ); - await curveAMOStrategy.connect(impersonatedVaultSigner).depositAll(); + await mintAndDepositToStrategy(); + + const checkBalanceAfter = await curveAMOStrategy.checkBalance( + weth.address + ); + log(`AMO checkBalance after deposit ${formatUnits(checkBalanceAfter)}`); + log( + `AMO checkBalance diff ${formatUnits( + checkBalanceAfter.sub(checkBalanceBefore) + )}` + ); expect( await curveAMOStrategy.checkBalance(weth.address) - ).to.approxEqualTolerance(defaultDeposit.mul(2)); + ).to.approxEqualTolerance(defaultDeposit.mul(3).add(checkBalanceBefore)); expect( await curveGauge.balanceOf(curveAMOStrategy.address) - ).to.approxEqualTolerance(defaultDeposit.mul(2)); + ).to.approxEqualTolerance(defaultDeposit.mul(3).add(gaugeTokensBefore)); expect(await weth.balanceOf(curveAMOStrategy.address)).to.equal(0); }); @@ -701,6 +734,7 @@ describe("Curve AMO strategy", function () { ...fixture, // Contracts strategy: curveAMOStrategy, + checkWithdrawAmounts: false, vault: oethbVault, assets: [weth], timelock: timelock, @@ -740,6 +774,7 @@ describe("Curve AMO strategy", function () { await oethbVault.connect(user).mint(weth.address, amount, amount); const gov = await oethbVault.governor(); + log(`Depositing ${formatUnits(amount)} WETH to AMO strategy`); const tx = await oethbVault .connect(await impersonateAndFund(gov)) .depositToStrategy(curveAMOStrategy.address, [weth.address], [amount]); @@ -785,6 +820,9 @@ describe("Curve AMO strategy", function () { } balances = await curvePool.get_balances(); + log(`Balanced Curve pool`); + log(`WETH balance: ${formatUnits(balances[0])}`); + log(`OETH balance: ${formatUnits(balances[1])}`); expect(balances[0]).to.approxEqualTolerance(balances[1]); }; @@ -808,6 +846,11 @@ describe("Curve AMO strategy", function () { ); } await weth.connect(nick).approve(curvePool.address, wethbAmount); + log( + `Adding ${formatUnits( + wethbAmount + )} WETH to Curve pool to make it unbalanced` + ); // prettier-ignore await curvePool .connect(nick)["add_liquidity(uint256[],uint256)"]([wethbAmount, 0], 0); @@ -826,10 +869,20 @@ describe("Curve AMO strategy", function () { .connect(nick) .mint(weth.address, oethbAmount, oethbAmount); await oethb.connect(nick).approve(curvePool.address, oethbAmount); + log( + `Adding ${formatUnits( + oethbAmount + )} OETH to Curve pool to make it unbalanced` + ); // prettier-ignore await curvePool .connect(nick)["add_liquidity(uint256[],uint256)"]([0, oethbAmount], 0); } + + const balances = await curvePool.get_balances(); + log(`Curve pool balances:`); + log(`WETH: ${formatUnits(balances[0])}`); + log(`OETH: ${formatUnits(balances[1])}`); }; const simulateCRVInflation = async ({ diff --git a/contracts/test/strategies/sonic/curve-amo.sonic.fork-test.js b/contracts/test/strategies/sonic/curve-amo.sonic.fork-test.js new file mode 100644 index 0000000000..15d57ea487 --- /dev/null +++ b/contracts/test/strategies/sonic/curve-amo.sonic.fork-test.js @@ -0,0 +1,765 @@ +const { createFixtureLoader } = require("../../_fixture"); +const { defaultSonicFixture } = require("../../_fixture-sonic"); +const { expect } = require("chai"); +const { oethUnits } = require("../../helpers"); +const addresses = require("../../../utils/addresses"); +const { impersonateAndFund } = require("../../../utils/signers"); +const { setERC20TokenBalance } = require("../../_fund"); +const hre = require("hardhat"); +const { advanceTime } = require("../../helpers"); +const { shouldBehaveLikeGovernable } = require("../../behaviour/governable"); +const { shouldBehaveLikeHarvestable } = require("../../behaviour/harvestable"); +const { shouldBehaveLikeStrategy } = require("../../behaviour/strategy"); + +const sonicFixture = createFixtureLoader(defaultSonicFixture); + +describe("Sonic Fork Test: Curve AMO strategy", function () { + let fixture, vault, curveAMOStrategy, os, ws, nick, clement, rafael, timelock; + + let curvePool, + curveGauge, + impersonatedVaultSigner, + impersonatedStrategist, + impersonatedHarvester, + impersonatedCurveGaugeFactory, + impersonatedAMOGovernor, + impersonatedCurveStrategy, + curveChildLiquidityGaugeFactory, + impersonatedTimelock, + crv, + harvester; + + let defaultDepositor; + + const defaultDeposit = oethUnits("5"); + + beforeEach(async () => { + fixture = await sonicFixture(); + vault = fixture.oSonicVault; + curveAMOStrategy = fixture.curveAMOStrategy; + os = fixture.oSonic; + ws = fixture.wS; + nick = fixture.nick; + rafael = fixture.rafael; + clement = fixture.clement; + timelock = fixture.timelock; + curvePool = fixture.curvePool; + curveGauge = fixture.curveGauge; + curveChildLiquidityGaugeFactory = fixture.curveChildLiquidityGaugeFactory; + crv = fixture.crv; + harvester = fixture.harvester; + + defaultDepositor = rafael; + + impersonatedVaultSigner = await impersonateAndFund(vault.address); + impersonatedStrategist = await impersonateAndFund( + await vault.strategistAddr() + ); + impersonatedHarvester = await impersonateAndFund(harvester.address); + impersonatedCurveGaugeFactory = await impersonateAndFund( + curveChildLiquidityGaugeFactory.address + ); + impersonatedAMOGovernor = await impersonateAndFund( + await curveAMOStrategy.governor() + ); + impersonatedTimelock = await impersonateAndFund(timelock.address); + impersonatedCurveStrategy = await impersonateAndFund( + curveAMOStrategy.address + ); + + // Set vaultBuffer to 100% + await vault.connect(impersonatedTimelock).setVaultBuffer(oethUnits("1")); + }); + + it("Should have correct parameters after deployment", async () => { + const { curveAMOStrategy } = fixture; + expect(await curveAMOStrategy.platformAddress()).to.equal( + addresses.sonic.WS_OS.pool + ); + expect(await curveAMOStrategy.vaultAddress()).to.equal(vault.address); + expect(await curveAMOStrategy.gauge()).to.equal( + addresses.sonic.WS_OS.gauge + ); + expect(await curveAMOStrategy.curvePool()).to.equal( + addresses.sonic.WS_OS.pool + ); + expect(await curveAMOStrategy.lpToken()).to.equal( + addresses.sonic.WS_OS.pool + ); + expect(await curveAMOStrategy.oeth()).to.equal(os.address); + expect(await curveAMOStrategy.weth()).to.equal(ws.address); + expect(await curveAMOStrategy.governor()).to.equal( + addresses.sonic.timelock + ); + expect(await curveAMOStrategy.rewardTokenAddresses(0)).to.equal( + addresses.sonic.CRV + ); + expect(await curveAMOStrategy.maxSlippage()).to.equal(oethUnits("0.002")); + }); + + describe("Operational functions", () => { + it("Should deposit to strategy", async () => { + await balancePool(); + + const checkBalanceBefore = await curveAMOStrategy.checkBalance( + ws.address + ); + const gaugeBalanceBefore = await curveGauge.balanceOf( + curveAMOStrategy.address + ); + await mintAndDepositToStrategy(); + + expect( + (await curveAMOStrategy.checkBalance(ws.address)).sub( + checkBalanceBefore + ) + ).to.approxEqual(defaultDeposit.mul(2)); + expect( + (await curveGauge.balanceOf(curveAMOStrategy.address)).sub( + gaugeBalanceBefore + ) + ).to.approxEqualTolerance(defaultDeposit.mul(2)); + expect(await os.balanceOf(defaultDepositor.address)).to.equal( + defaultDeposit + ); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal(0); + }); + + it("Should deposit all to strategy", async () => { + await balancePool(); + + const amount = defaultDeposit; + const user = defaultDepositor; + const checkBalanceBefore = await curveAMOStrategy.checkBalance( + ws.address + ); + const gaugeBalanceBefore = await curveGauge.balanceOf( + curveAMOStrategy.address + ); + + const balance = await ws.balanceOf(user.address); + if (balance < amount) { + await setERC20TokenBalance(user.address, ws, amount + balance, hre); + } + await ws.connect(user).transfer(curveAMOStrategy.address, amount); + + expect(await ws.balanceOf(curveAMOStrategy.address)).to.gt(0); + await curveAMOStrategy.connect(impersonatedVaultSigner).depositAll(); + + expect( + (await curveAMOStrategy.checkBalance(ws.address)).sub( + checkBalanceBefore + ) + ).to.approxEqualTolerance(defaultDeposit.mul(2)); + expect( + (await curveGauge.balanceOf(curveAMOStrategy.address)).sub( + gaugeBalanceBefore + ) + ).to.approxEqualTolerance(defaultDeposit.mul(2)); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal(0); + }); + + it("Should deposit all to strategy with no balance", async () => { + await balancePool(); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal(0); + + await curveAMOStrategy.connect(impersonatedVaultSigner).depositAll(); + + expect(await curveAMOStrategy.checkBalance(ws.address)).to.eq(0); + expect(await curveGauge.balanceOf(curveAMOStrategy.address)).to.eq(0); + }); + + it("Should withdraw from strategy", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + + const impersonatedVaultSigner = await impersonateAndFund(vault.address); + + const checkBalanceBefore = await curveAMOStrategy.checkBalance( + ws.address + ); + const gaugeBalanceBefore = await curveGauge.balanceOf( + curveAMOStrategy.address + ); + + await curveAMOStrategy + .connect(impersonatedVaultSigner) + .withdraw(vault.address, ws.address, oethUnits("1")); + + expect( + checkBalanceBefore.sub(await curveAMOStrategy.checkBalance(ws.address)) + ).to.approxEqualTolerance(oethUnits("1").mul(2)); + expect( + gaugeBalanceBefore.sub( + await curveGauge.balanceOf(curveAMOStrategy.address) + ) + ).to.approxEqualTolerance(oethUnits("1").mul(2)); + expect(await os.balanceOf(curveAMOStrategy.address)).to.equal(0); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal( + oethUnits("0") + ); + }); + + it("Should withdraw all from strategy", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + + const balanceVault = await ws.balanceOf(vault.address); + + await curveAMOStrategy.connect(impersonatedVaultSigner).withdrawAll(); + + expect( + await curveAMOStrategy.checkBalance(ws.address) + ).to.approxEqualTolerance(0); + expect( + await curveGauge.balanceOf(curveAMOStrategy.address) + ).to.approxEqualTolerance(0); + expect(await os.balanceOf(curveAMOStrategy.address)).to.equal(0); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal( + oethUnits("0") + ); + expect(await ws.balanceOf(vault.address)).to.approxEqualTolerance( + balanceVault.add(defaultDeposit) + ); + }); + + it("Should mintAndAddOToken", async () => { + await unbalancePool({ + balancedBefore: true, + wsAmount: defaultDeposit, + }); + + await curveAMOStrategy + .connect(impersonatedStrategist) + .mintAndAddOTokens(defaultDeposit); + + expect( + await curveAMOStrategy.checkBalance(ws.address) + ).to.approxEqualTolerance(defaultDeposit); + expect( + await curveGauge.balanceOf(curveAMOStrategy.address) + ).to.approxEqualTolerance(defaultDeposit); + expect(await os.balanceOf(curveAMOStrategy.address)).to.equal(0); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal( + oethUnits("0") + ); + }); + + it("Should removeAndBurnOToken", async () => { + await balancePool(); + await mintAndDepositToStrategy({ + userOverride: false, + amount: defaultDeposit.mul(2), + returnTransaction: false, + }); + await unbalancePool({ + balancedBefore: true, + osAmount: defaultDeposit.mul(2), + }); + + await curveAMOStrategy + .connect(impersonatedStrategist) + .removeAndBurnOTokens(defaultDeposit); + + expect( + await curveAMOStrategy.checkBalance(ws.address) + ).to.approxEqualTolerance(defaultDeposit.mul(4).sub(defaultDeposit)); + expect( + await curveGauge.balanceOf(curveAMOStrategy.address) + ).to.approxEqualTolerance(defaultDeposit.mul(4).sub(defaultDeposit)); + expect(await os.balanceOf(curveAMOStrategy.address)).to.equal(0); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal( + oethUnits("0") + ); + }); + + it("Should removeOnlyAssets", async () => { + await balancePool(); + await mintAndDepositToStrategy({ + userOverride: false, + amount: defaultDeposit.mul(2), + returnTransaction: false, + }); + await unbalancePool({ + balancedBefore: true, + wsAmount: defaultDeposit.mul(2), + }); + + const vaultETHBalanceBefore = await ws.balanceOf(vault.address); + + await curveAMOStrategy + .connect(impersonatedStrategist) + .removeOnlyAssets(defaultDeposit); + + expect( + await curveAMOStrategy.checkBalance(ws.address) + ).to.approxEqualTolerance(defaultDeposit.mul(4).sub(defaultDeposit)); + expect( + await curveGauge.balanceOf(curveAMOStrategy.address) + ).to.approxEqualTolerance(defaultDeposit.mul(4).sub(defaultDeposit)); + expect(await ws.balanceOf(vault.address)).to.approxEqualTolerance( + vaultETHBalanceBefore.add(defaultDeposit) + ); + }); + + it("Should collectRewardTokens", async () => { + await mintAndDepositToStrategy(); + await simulateCRVInflation({ + amount: oethUnits("1000000"), + timejump: 60, + checkpoint: true, + }); + + const balanceCRVHarvesterBefore = await crv.balanceOf(harvester.address); + await curveAMOStrategy + .connect(impersonatedHarvester) + .collectRewardTokens(); + const balanceCRVHarvesterAfter = await crv.balanceOf(harvester.address); + + expect(balanceCRVHarvesterAfter).to.be.gt(balanceCRVHarvesterBefore); + expect(await crv.balanceOf(curveGauge.address)).to.equal(0); + }); + }); + + describe("when pool is heavily unbalanced", () => { + it("Should deposit with OS", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + + await unbalancePool({ osAmount: defaultDeposit.mul(1000) }); + + await curveAMOStrategy.connect(impersonatedVaultSigner).depositAll(); + + expect( + await curveAMOStrategy.checkBalance(ws.address) + ).to.approxEqualTolerance(defaultDeposit.mul(2)); + expect( + await curveGauge.balanceOf(curveAMOStrategy.address) + ).to.approxEqualTolerance(defaultDeposit.mul(2)); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal(0); + }); + + it("Should deposit with wS", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + + await unbalancePool({ wsAmount: defaultDeposit.mul(1000) }); + + await curveAMOStrategy.connect(impersonatedVaultSigner).depositAll(); + + expect( + await curveAMOStrategy.checkBalance(ws.address) + ).to.approxEqualTolerance(defaultDeposit.mul(2)); + expect( + await curveGauge.balanceOf(curveAMOStrategy.address) + ).to.approxEqualTolerance(defaultDeposit.mul(2)); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal(0); + }); + + it("Should withdraw all with OS", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + + await unbalancePool({ osAmount: defaultDeposit.mul(1000) }); + + const checkBalanceAMO = await curveAMOStrategy.checkBalance(ws.address); + const balanceVault = await ws.balanceOf(vault.address); + + await curveAMOStrategy.connect(impersonatedVaultSigner).withdrawAll(); + + expect( + await curveAMOStrategy.checkBalance(ws.address) + ).to.approxEqualTolerance(0); + expect( + await curveGauge.balanceOf(curveAMOStrategy.address) + ).to.approxEqualTolerance(0); + expect(await os.balanceOf(curveAMOStrategy.address)).to.equal(0); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal( + oethUnits("0") + ); + expect(await ws.balanceOf(vault.address)).to.approxEqualTolerance( + balanceVault.add(checkBalanceAMO) + ); + }); + + it("Should withdraw all with wS", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + + await unbalancePool({ wsAmount: defaultDeposit.mul(1000) }); + const checkBalanceAMO = await curveAMOStrategy.checkBalance(ws.address); + const balanceVault = await ws.balanceOf(vault.address); + + await curveAMOStrategy.connect(impersonatedVaultSigner).withdrawAll(); + + expect( + await curveAMOStrategy.checkBalance(ws.address) + ).to.approxEqualTolerance(0); + expect( + await curveGauge.balanceOf(curveAMOStrategy.address) + ).to.approxEqualTolerance(0); + expect(await os.balanceOf(curveAMOStrategy.address)).to.equal(0); + expect(await ws.balanceOf(curveAMOStrategy.address)).to.equal( + oethUnits("0") + ); + expect(await ws.balanceOf(vault.address)).to.approxEqualTolerance( + balanceVault.add(checkBalanceAMO) + ); + }); + }); + + describe("admin functions", () => { + it("Should set max slippage", async () => { + await curveAMOStrategy + .connect(impersonatedAMOGovernor) + .setMaxSlippage(oethUnits("0.01456")); + + expect(await curveAMOStrategy.maxSlippage()).to.equal( + oethUnits("0.01456") + ); + }); + }); + + describe("Should revert when", () => { + it("Deposit: Must deposit something", async () => { + await expect( + curveAMOStrategy.connect(impersonatedVaultSigner).deposit(ws.address, 0) + ).to.be.revertedWith("Must deposit something"); + }); + it("Deposit: Can only deposit wS", async () => { + await expect( + curveAMOStrategy + .connect(impersonatedVaultSigner) + .deposit(os.address, defaultDeposit) + ).to.be.revertedWith("Can only deposit WETH"); + }); + it("Deposit: Caller is not the Vault", async () => { + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .deposit(ws.address, defaultDeposit) + ).to.be.revertedWith("Caller is not the Vault"); + }); + it("Deposit: Protocol is insolvent", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + + // Make protocol insolvent by minting a lot of OETH + // This is a cheat. + // prettier-ignore + await vault + .connect(impersonatedCurveStrategy)["mintForStrategy(uint256)"](oethUnits("1000000")); + + await expect( + mintAndDepositToStrategy({ returnTransaction: true }) + ).to.be.revertedWith("Protocol insolvent"); + }); + it("Withdraw: Must withdraw something", async () => { + await expect( + curveAMOStrategy + .connect(impersonatedVaultSigner) + .withdraw(vault.address, ws.address, 0) + ).to.be.revertedWith("Must withdraw something"); + }); + it("Withdraw: Can only withdraw WETH", async () => { + await expect( + curveAMOStrategy + .connect(impersonatedVaultSigner) + .withdraw(vault.address, os.address, defaultDeposit) + ).to.be.revertedWith("Can only withdraw WETH"); + }); + it("Withdraw: Caller is not the vault", async () => { + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .withdraw(vault.address, ws.address, defaultDeposit) + ).to.be.revertedWith("Caller is not the Vault"); + }); + it("Withdraw: Amount is greater than balance", async () => { + await expect( + curveAMOStrategy + .connect(impersonatedVaultSigner) + .withdraw(vault.address, ws.address, oethUnits("1000000")) + ).to.be.revertedWith(""); + }); + it("Withdraw: Protocol is insolvent", async () => { + await balancePool(); + await mintAndDepositToStrategy({ amount: defaultDeposit.mul(2) }); + + // Make protocol insolvent by minting a lot of OETH and send them + // Otherwise they will be burned and the protocol will not be insolvent. + // This is a cheat. + // prettier-ignore + await vault + .connect(impersonatedCurveStrategy)["mintForStrategy(uint256)"](oethUnits("1000000")); + await os + .connect(impersonatedCurveStrategy) + .transfer(vault.address, oethUnits("1000000")); + + await expect( + curveAMOStrategy + .connect(impersonatedVaultSigner) + .withdraw(vault.address, ws.address, defaultDeposit) + ).to.be.revertedWith("Protocol insolvent"); + }); + it("Mint OToken: Asset overshot peg", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + await unbalancePool({ wsAmount: defaultDeposit }); // +5 WETH in the pool + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .mintAndAddOTokens(defaultDeposit.mul(2)) + ).to.be.revertedWith("Assets overshot peg"); + }); + it("Mint OToken: OTokens balance worse", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + await unbalancePool({ osAmount: defaultDeposit.mul(2) }); // +10 OETH in the pool + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .mintAndAddOTokens(defaultDeposit) + ).to.be.revertedWith("OTokens balance worse"); + }); + it("Mint OToken: Protocol insolvent", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + // prettier-ignore + await vault + .connect(impersonatedCurveStrategy)["mintForStrategy(uint256)"](oethUnits("1000000")); + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .mintAndAddOTokens(defaultDeposit) + ).to.be.revertedWith("Protocol insolvent"); + }); + it("Burn OToken: Asset balance worse", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + await unbalancePool({ wsAmount: defaultDeposit.mul(2) }); // +10 WETH in the pool + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .removeAndBurnOTokens(defaultDeposit) + ).to.be.revertedWith("Assets balance worse"); + }); + it("Burn OToken: OTokens overshot peg", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + await unbalancePool({ osAmount: defaultDeposit }); // +5 OETH in the pool + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .removeAndBurnOTokens(defaultDeposit) + ).to.be.revertedWith("OTokens overshot peg"); + }); + it("Burn OToken: Protocol insolvent", async () => { + await balancePool(); + await mintAndDepositToStrategy(); + // prettier-ignore + await vault + .connect(impersonatedCurveStrategy)["mintForStrategy(uint256)"](oethUnits("1000000")); + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .removeAndBurnOTokens(defaultDeposit) + ).to.be.revertedWith("Protocol insolvent"); + }); + it("Remove only assets: Asset overshot peg", async () => { + await balancePool(); + await mintAndDepositToStrategy({ amount: defaultDeposit.mul(2) }); + await unbalancePool({ wsAmount: defaultDeposit.mul(2) }); // +10 WETH in the pool + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .removeOnlyAssets(defaultDeposit.mul(3)) + ).to.be.revertedWith("Assets overshot peg"); + }); + it("Remove only assets: OTokens balance worse", async () => { + await balancePool(); + await mintAndDepositToStrategy({ amount: defaultDeposit.mul(2) }); + await unbalancePool({ osAmount: defaultDeposit.mul(2) }); // +10 OETH in the pool + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .removeOnlyAssets(defaultDeposit) + ).to.be.revertedWith("OTokens balance worse"); + }); + it("Remove only assets: Protocol insolvent", async () => { + await balancePool(); + await mintAndDepositToStrategy({ amount: defaultDeposit.mul(2) }); + // prettier-ignore + await vault + .connect(impersonatedCurveStrategy)["mintForStrategy(uint256)"](oethUnits("1000000")); + await expect( + curveAMOStrategy + .connect(impersonatedStrategist) + .removeOnlyAssets(defaultDeposit) + ).to.be.revertedWith("Protocol insolvent"); + }); + it("Check balance: Unsupported asset", async () => { + await expect( + curveAMOStrategy.checkBalance(os.address) + ).to.be.revertedWith("Unsupported asset"); + }); + it("Max slippage is too high", async () => { + await expect( + curveAMOStrategy + .connect(impersonatedAMOGovernor) + .setMaxSlippage(oethUnits("0.51")) + ).to.be.revertedWith("Slippage must be less than 100%"); + }); + }); + + shouldBehaveLikeGovernable(() => ({ + ...fixture, + anna: rafael, + josh: nick, + matt: clement, + dai: crv, + strategy: curveAMOStrategy, + governor: timelock, + })); + + shouldBehaveLikeHarvestable(() => ({ + ...fixture, + anna: rafael, + strategy: curveAMOStrategy, + harvester, + oeth: os, + governor: timelock, + })); + + shouldBehaveLikeStrategy(() => ({ + ...fixture, + // Contracts + strategy: curveAMOStrategy, + checkWithdrawAmounts: false, + vault: vault, + assets: [ws], + governor: timelock, + strategist: rafael, + harvester, + crv, + // As we don't have this on base fixture, we use CRV + usdt: crv, + usdc: crv, + dai: crv, + weth: ws, + reth: crv, + stETH: crv, + frxETH: crv, + cvx: crv, + comp: crv, + bal: crv, + // Users + anna: rafael, + matt: clement, + josh: nick, + })); + + const mintAndDepositToStrategy = async ({ + userOverride, + amount, + returnTransaction, + } = {}) => { + const user = userOverride || defaultDepositor; + amount = amount || defaultDeposit; + + const balance = await ws.balanceOf(user.address); + if (balance < amount) { + await setERC20TokenBalance(user.address, ws, amount + balance, hre); + } + await ws.connect(user).approve(vault.address, amount); + await vault.connect(user).mint(ws.address, amount, amount); + + const gov = await vault.governor(); + const tx = await vault + .connect(await impersonateAndFund(gov)) + .depositToStrategy(curveAMOStrategy.address, [ws.address], [amount]); + + if (returnTransaction) { + return tx; + } + + await expect(tx).to.emit(curveAMOStrategy, "Deposit"); + }; + + const balancePool = async () => { + let balances = await curvePool.get_balances(); + const balanceOS = balances[0]; + const balanceWS = balances[1]; + + if (balanceWS > balanceOS) { + const amount = balanceWS.sub(balanceOS); + const balance = ws.balanceOf(nick.address); + if (balance < amount) { + await setERC20TokenBalance(nick.address, ws, amount + balance, hre); + } + await ws.connect(nick).approve(vault.address, amount.mul(101).div(10)); + await vault + .connect(nick) + .mint(ws.address, amount.mul(101).div(10), amount); + await os.connect(nick).approve(curvePool.address, amount); + // prettier-ignore + await curvePool + .connect(nick)["add_liquidity(uint256[],uint256)"]([amount, 0], 0); + } else if (balanceWS < balanceOS) { + const amount = balanceOS.sub(balanceWS); + const balance = ws.balanceOf(nick.address); + if (balance < amount) { + await setERC20TokenBalance(nick.address, ws, amount + balance, hre); + } + await ws.connect(nick).approve(curvePool.address, amount); + // prettier-ignore + await curvePool + .connect(nick)["add_liquidity(uint256[],uint256)"]([0, amount], 0); + } + + balances = await curvePool.get_balances(); + expect(balances[0]).to.approxEqualTolerance(balances[1]); + }; + + const unbalancePool = async ({ balancedBefore, wsAmount, osAmount } = {}) => { + if (balancedBefore) { + await balancePool(); + } + + if (wsAmount) { + const balance = ws.balanceOf(nick.address); + if (balance < wsAmount) { + await setERC20TokenBalance(nick.address, ws, wsAmount + balance, hre); + } + await ws.connect(nick).approve(curvePool.address, wsAmount); + // prettier-ignore + await curvePool + .connect(nick)["add_liquidity(uint256[],uint256)"]([0, wsAmount], 0); + } else { + const balance = ws.balanceOf(nick.address); + if (balance < osAmount) { + await setERC20TokenBalance(nick.address, ws, osAmount + balance, hre); + } + await ws.connect(nick).approve(vault.address, osAmount); + await vault.connect(nick).mint(ws.address, osAmount, osAmount); + await os.connect(nick).approve(curvePool.address, osAmount); + // prettier-ignore + await curvePool + .connect(nick)["add_liquidity(uint256[],uint256)"]([osAmount, 0], 0); + } + }; + + const simulateCRVInflation = async ({ + amount, + timejump, + checkpoint, + } = {}) => { + await setERC20TokenBalance(curveGauge.address, crv, amount, hre); + await advanceTime(timejump); + if (checkpoint) { + curveGauge + .connect(impersonatedCurveGaugeFactory) + .user_checkpoint(curveAMOStrategy.address); + } + }; +}); diff --git a/contracts/test/strategies/sonicStaking.sonic.fork-test.js b/contracts/test/strategies/sonic/sonicStaking.sonic.fork-test.js similarity index 68% rename from contracts/test/strategies/sonicStaking.sonic.fork-test.js rename to contracts/test/strategies/sonic/sonicStaking.sonic.fork-test.js index ff761cc08c..70f9974439 100644 --- a/contracts/test/strategies/sonicStaking.sonic.fork-test.js +++ b/contracts/test/strategies/sonic/sonicStaking.sonic.fork-test.js @@ -1,10 +1,10 @@ -const { defaultSonicFixture } = require("./../_fixture-sonic"); +const { defaultSonicFixture } = require("../../_fixture-sonic"); const { shouldBehaveLikeASFCStakingStrategy, -} = require("../behaviour/sfcStakingStrategy"); -const addresses = require("../../utils/addresses"); +} = require("../../behaviour/sfcStakingStrategy"); +const addresses = require("../../../utils/addresses"); -describe("Sonic ForkTest: Sonic Staking Strategy", function () { +describe("Sonic Fork Test: Sonic Staking Strategy", function () { this.timeout(0); let fixture; diff --git a/contracts/test/strategies/sonic/swapx-amo.sonic.fork-test.js b/contracts/test/strategies/sonic/swapx-amo.sonic.fork-test.js new file mode 100644 index 0000000000..a6f4f825ff --- /dev/null +++ b/contracts/test/strategies/sonic/swapx-amo.sonic.fork-test.js @@ -0,0 +1,1034 @@ +const { expect } = require("chai"); +const { formatUnits, parseUnits } = require("ethers/lib/utils"); + +const { createFixtureLoader } = require("../../_fixture"); +const { swapXAMOFixture } = require("../../_fixture-sonic"); +const { isCI } = require("../../helpers"); +const addresses = require("../../../utils/addresses"); + +const log = require("../../../utils/logger")("test:fork:sonic:swapx:amo"); + +describe("Sonic ForkTest: SwapX AMO Strategy", function () { + // Retry up to 3 times on CI + this.retries(isCI ? 3 : 0); + + let fixture; + + describe("post deployment", () => { + const loadFixture = createFixtureLoader(swapXAMOFixture); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Should have constants and immutables set", async () => { + const { swapXAMOStrategy } = fixture; + + expect(await swapXAMOStrategy.SOLVENCY_THRESHOLD()).to.equal( + parseUnits("0.998", 18) + ); + expect(await swapXAMOStrategy.ws()).to.equal(addresses.sonic.wS); + expect(await swapXAMOStrategy.os()).to.equal(addresses.sonic.OSonicProxy); + expect(await swapXAMOStrategy.pool()).to.equal( + addresses.sonic.SwapXWSOS.pool + ); + expect(await swapXAMOStrategy.gauge()).to.equal( + addresses.sonic.SwapXWSOS.gauge + ); + expect(await swapXAMOStrategy.governor()).to.equal( + addresses.sonic.timelock + ); + expect(await swapXAMOStrategy.supportsAsset(addresses.sonic.wS)).to.true; + }); + it("Should be able to check balance", async () => { + const { wS, nick, swapXAMOStrategy } = fixture; + + const balance = await swapXAMOStrategy.checkBalance(wS.address); + log(`check balance ${balance}`); + expect(balance).gte(0); + + // This uses a transaction to call a view function so the gas usage can be reported. + const tx = await swapXAMOStrategy + .connect(nick) + .populateTransaction.checkBalance(wS.address); + await nick.sendTransaction(tx); + }); + it("Only Governor can approve all tokens", async () => { + const { + timelock, + strategist, + nick, + oSonicVaultSigner, + swapXAMOStrategy, + wS, + oSonic, + swapXPool, + } = fixture; + + expect(await swapXAMOStrategy.connect(timelock).isGovernor()).to.equal( + true + ); + + // Timelock can approve all tokens + const tx = await swapXAMOStrategy + .connect(timelock) + .safeApproveAllTokens(); + await expect(tx).to.emit(wS, "Approval"); + await expect(tx).to.emit(oSonic, "Approval"); + await expect(tx).to.emit(swapXPool, "Approval"); + + for (const signer of [strategist, nick, oSonicVaultSigner]) { + const tx = swapXAMOStrategy.connect(signer).safeApproveAllTokens(); + await expect(tx).to.be.revertedWith("Caller is not the Governor"); + } + }); + }); + + describe("with wS in the vault", () => { + const loadFixture = createFixtureLoader(swapXAMOFixture, { + wsMintAmount: 5000, + depositToStrategy: false, + balancePool: true, + }); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Vault should deposit wS to AMO strategy", async function () { + await assertDeposit(parseUnits("2000")); + }); + it("Only vault can deposit wS to AMO strategy", async function () { + const { + swapXAMOStrategy, + oSonicVaultSigner, + strategist, + timelock, + nick, + wS, + } = fixture; + + const depositAmount = parseUnits("50"); + await wS + .connect(oSonicVaultSigner) + .transfer(swapXAMOStrategy.address, depositAmount); + + for (const signer of [strategist, timelock, nick]) { + const tx = swapXAMOStrategy + .connect(signer) + .deposit(wS.address, depositAmount); + + await expect(tx).to.revertedWith("Caller is not the Vault"); + } + }); + it("Only vault can deposit all wS to AMO strategy", async function () { + const { + swapXAMOStrategy, + swapXPool, + oSonicVaultSigner, + strategist, + timelock, + nick, + wS, + } = fixture; + + const depositAmount = parseUnits("50"); + await wS + .connect(oSonicVaultSigner) + .transfer(swapXAMOStrategy.address, depositAmount); + + for (const signer of [strategist, timelock, nick]) { + const tx = swapXAMOStrategy.connect(signer).depositAll(); + + await expect(tx).to.revertedWith("Caller is not the Vault"); + } + + const tx = await swapXAMOStrategy.connect(oSonicVaultSigner).depositAll(); + await expect(tx) + .to.emit(swapXAMOStrategy, "Deposit") + .withNamedArgs({ _asset: wS.address, _pToken: swapXPool.address }); + }); + }); + + describe("with the strategy having OS and wS in a balanced pool", () => { + const loadFixture = createFixtureLoader(swapXAMOFixture, { + wsMintAmount: 5000, + depositToStrategy: true, + balancePool: true, + }); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Vault should deposit wS to AMO strategy", async function () { + await assertDeposit(parseUnits("5000")); + }); + it("Vault should be able to withdraw all", async () => { + await assertWithdrawAll(); + }); + it("Vault should be able to partially withdraw", async () => { + await assertWithdrawPartial(parseUnits("1000")); + }); + it("Only vault can withdraw wS from AMO strategy", async function () { + const { swapXAMOStrategy, oSonicVault, strategist, timelock, nick, wS } = + fixture; + + for (const signer of [strategist, timelock, nick]) { + const tx = swapXAMOStrategy + .connect(signer) + .withdraw(oSonicVault.address, wS.address, parseUnits("50")); + + await expect(tx).to.revertedWith("Caller is not the Vault"); + } + }); + it("Only vault and governor can withdraw all WETH from AMO strategy", async function () { + const { swapXAMOStrategy, strategist, timelock, nick } = fixture; + + for (const signer of [strategist, nick]) { + const tx = swapXAMOStrategy.connect(signer).withdrawAll(); + + await expect(tx).to.revertedWith("Caller is not the Vault or Governor"); + } + + // Governor can withdraw all + const tx = swapXAMOStrategy.connect(timelock).withdrawAll(); + await expect(tx).to.emit(swapXAMOStrategy, "Withdrawal"); + }); + }); + + describe("with a lot more OS in the pool", () => { + const loadFixture = createFixtureLoader(swapXAMOFixture, { + wsMintAmount: 5000, + depositToStrategy: true, + balancePool: true, + poolAddOSAmount: 60000, + }); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Vault should deposit wS to AMO strategy", async function () { + await assertDeposit(parseUnits("5000")); + }); + it("Vault should be able to withdraw all", async () => { + await assertWithdrawAll(); + }); + it("Vault should be able to partially withdraw", async () => { + await assertWithdrawPartial(parseUnits("4000")); + }); + it("Strategist should swap a little assets to the pool", async () => { + await assertSwapAssetsToPool(parseUnits("3")); + }); + it("Strategist should swap a lot of assets to the pool", async () => { + await assertSwapAssetsToPool(parseUnits("3000")); + }); + it("Strategist should swap most of the wS owned by the strategy", async () => { + // TODO calculate how much wS should be swapped to get the pool balanced + await assertSwapAssetsToPool(parseUnits("4400")); + }); + it("Strategist should fail to swap all wS owned by the strategy", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + const tx = swapXAMOStrategy + .connect(strategist) + .swapAssetsToPool(parseUnits("5500")); + + await expect(tx).to.be.revertedWith("Assets overshot peg"); + }); + it("Strategist should fail to add more wS than owned by the strategy", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + const tx = swapXAMOStrategy + .connect(strategist) + .swapAssetsToPool(parseUnits("20000")); + + await expect(tx).to.be.revertedWith("Not enough LP tokens in gauge"); + }); + it("Strategist should fail to add more OS to the pool", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + // try swapping OS into the pool + const tx = swapXAMOStrategy + .connect(strategist) + .swapOTokensToPool(parseUnits("0.001")); + + await expect(tx).to.be.revertedWith("OTokens balance worse"); + }); + }); + + describe("with a little more OS in the pool", () => { + const loadFixture = createFixtureLoader(swapXAMOFixture, { + wsMintAmount: 20000, + depositToStrategy: true, + balancePool: true, + poolAddOSAmount: 500, + }); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Vault should deposit wS to AMO strategy", async function () { + await assertDeposit(parseUnits("12000")); + }); + it("Vault should be able to withdraw all", async () => { + await assertWithdrawAll(); + }); + it("Vault should be able to partially withdraw", async () => { + await assertWithdrawPartial(parseUnits("1000")); + }); + it("Strategist should swap a little assets to the pool", async () => { + await assertSwapAssetsToPool(parseUnits("3")); + }); + it("Strategist should swap enough wS to get the pool close to balanced", async () => { + // just under half the extra OS amount + const osAmount = parseUnits("247"); + await assertSwapAssetsToPool(osAmount); + }); + it("Strategist should fail to add too much wS to the pool", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + // try swapping half the extra OS in the pool + const tx = swapXAMOStrategy + .connect(strategist) + .swapAssetsToPool(parseUnits("250")); + + await expect(tx).to.be.revertedWith("Assets overshot peg"); + }); + it("Strategist should fail to add zero wS to the pool", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + const tx = swapXAMOStrategy.connect(strategist).swapAssetsToPool(0); + + await expect(tx).to.be.revertedWith("Must swap something"); + }); + it("Strategist should fail to add more OS to the pool", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + // try swapping OS into the pool + const tx = swapXAMOStrategy + .connect(strategist) + .swapOTokensToPool(parseUnits("0.001")); + + await expect(tx).to.be.revertedWith("OTokens balance worse"); + }); + }); + + describe("with a lot more wS in the pool", () => { + const loadFixture = createFixtureLoader(swapXAMOFixture, { + wsMintAmount: 5000, + depositToStrategy: true, + balancePool: true, + poolAddwSAmount: 20000, + }); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Vault should deposit wS to AMO strategy", async function () { + await assertDeposit(parseUnits("6000")); + }); + it("Vault should be able to withdraw all", async () => { + await assertWithdrawAll(); + }); + it("Vault should be able to partially withdraw", async () => { + await assertWithdrawPartial(parseUnits("1000")); + }); + it("Strategist should swap a little OS to the pool", async () => { + const osAmount = parseUnits("0.3"); + await assertSwapOTokensToPool(osAmount, fixture); + }); + it("Strategist should swap a lot of OS to the pool", async () => { + const osAmount = parseUnits("5000"); + await assertSwapOTokensToPool(osAmount, fixture); + }); + it("Strategist should get the pool close to balanced", async () => { + // just under half the extra wS amount + const osAmount = parseUnits("9000"); + await assertSwapOTokensToPool(osAmount, fixture); + }); + it("Strategist should fail to add so much OS that is overshoots", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + // try swapping wS into the pool + const tx = swapXAMOStrategy + .connect(strategist) + .swapOTokensToPool(parseUnits("9990")); + + await expect(tx).to.be.revertedWith("OTokens overshot peg"); + }); + it("Strategist should fail to add more wS to the pool", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + // try swapping wS into the pool + const tx = swapXAMOStrategy + .connect(strategist) + .swapAssetsToPool(parseUnits("0.0001")); + + await expect(tx).to.be.revertedWith("Assets balance worse"); + }); + }); + + describe("with a little more wS in the pool", () => { + const loadFixture = createFixtureLoader(swapXAMOFixture, { + wsMintAmount: 20000, + depositToStrategy: true, + balancePool: true, + poolAddwSAmount: 200, + }); + beforeEach(async () => { + fixture = await loadFixture(); + }); + it("Vault should deposit wS to AMO strategy", async function () { + await assertDeposit(parseUnits("18000")); + }); + it("Vault should be able to withdraw all", async () => { + await assertWithdrawAll(); + }); + it("Vault should be able to partially withdraw", async () => { + await assertWithdrawPartial(parseUnits("1000")); + }); + it("Strategist should swap a little OS to the pool", async () => { + const osAmount = parseUnits("8"); + await assertSwapOTokensToPool(osAmount, fixture); + }); + it("Strategist should get the pool close to balanced", async () => { + // just under half the extra wS amount + const osAmount = parseUnits("99"); + await assertSwapOTokensToPool(osAmount, fixture); + }); + it("Strategist should fail to add zero OS to the pool", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + const tx = swapXAMOStrategy.connect(strategist).swapOTokensToPool(0); + + await expect(tx).to.be.revertedWith("Must swap something"); + }); + it("Strategist should fail to add too much OS to the pool", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + // Add OS to the pool + const tx = swapXAMOStrategy + .connect(strategist) + .swapOTokensToPool(parseUnits("110")); + + await expect(tx).to.be.revertedWith("OTokens overshot peg"); + }); + it("Strategist should fail to add more wS to the pool", async () => { + const { swapXAMOStrategy, strategist } = fixture; + + // try swapping wS into the pool + const tx = swapXAMOStrategy + .connect(strategist) + .swapAssetsToPool(parseUnits("0.0001")); + + await expect(tx).to.be.revertedWith("Assets balance worse"); + }); + }); + + describe("with the strategy owning a small percentage of the pool", () => { + const loadFixture = createFixtureLoader(swapXAMOFixture, { + wsMintAmount: 5000, + depositToStrategy: true, + balancePool: true, + }); + let dataBefore; + beforeEach(async () => { + fixture = await loadFixture(); + + const { clement, wS, oSonic, oSonicVault, swapXPool } = fixture; + + // Other users adds a lot more liquidity to the pool + const bigAmount = parseUnits("1000000"); + // transfer wS to the pool + await wS.connect(clement).transfer(swapXPool.address, bigAmount); + // Mint OS with wS + await oSonicVault.connect(clement).mint(wS.address, bigAmount.mul(5), 0); + // transfer OS to the pool + await oSonic.connect(clement).transfer(swapXPool.address, bigAmount); + // mint pool LP tokens + await swapXPool.connect(clement).mint(clement.address); + + dataBefore = await snapData(); + await logSnapData(dataBefore); + }); + it("a lot of OS is swapped into the pool", async () => { + const { oSonic, swapXAMOStrategy, wS } = fixture; + + // Swap OS into the pool and wS out + await poolSwapTokensIn(oSonic, parseUnits("1005000")); + + await logSnapData(await snapData(), "\nAfter swapping OS into the pool"); + + // Assert the strategy's balance + expect( + await swapXAMOStrategy.checkBalance(wS.address), + "Strategy's check balance" + ).to.withinRange(dataBefore.stratBalance, dataBefore.stratBalance.add(1)); + + // Swap wS into the pool and OS out + await poolSwapTokensIn(wS, parseUnits("2000000")); + + await logSnapData(await snapData(), "\nAfter swapping wS into the pool"); + + // Assert the strategy's balance + expect( + await swapXAMOStrategy.checkBalance(wS.address), + "Strategy's check balance" + ).to.withinRange(dataBefore.stratBalance, dataBefore.stratBalance.add(1)); + }); + it("a lot of wS is swapped into the pool", async () => { + const { swapXAMOStrategy, oSonic, wS } = fixture; + + // Swap wS into the pool and OS out + await poolSwapTokensIn(wS, parseUnits("1006000")); + + await logSnapData(await snapData(), "\nAfter swapping wS into the pool"); + + // Assert the strategy's balance + expect( + await swapXAMOStrategy.checkBalance(wS.address), + "Strategy's check balance" + ).to.withinRange(dataBefore.stratBalance, dataBefore.stratBalance.add(1)); + + // Swap OS into the pool and wS out + await poolSwapTokensIn(oSonic, parseUnits("1005000")); + + await logSnapData(await snapData(), "\nAfter swapping OS into the pool"); + + // Assert the strategy's balance + expect( + await swapXAMOStrategy.checkBalance(wS.address), + "Strategy's check balance" + ).to.withinRange(dataBefore.stratBalance, dataBefore.stratBalance.add(1)); + }); + }); + + const poolSwapTokensIn = async (tokenIn, amountIn) => { + const { clement, swapXPool, wS } = fixture; + const amountOut = await swapXPool.getAmountOut(amountIn, tokenIn.address); + await tokenIn.connect(clement).transfer(swapXPool.address, amountIn); + if (tokenIn.address == wS.address) { + await swapXPool.swap(0, amountOut, clement.address, "0x"); + } else { + await swapXPool.swap(amountOut, 0, clement.address, "0x"); + } + }; + + const precision = parseUnits("1", 18); + // Calculate the value of wS and OS tokens assuming the pool is balanced + const calcReserveValue = (reserves) => { + const k = calcInvariant(reserves); + + // If x = y, let’s denote x = y = z (where z is the common reserve value) + // Substitute z into the invariant: + // k = z^3 * z + z * z^3 + // k = 2 * z^4 + // Going back the other way to calculate the common reserve value z + // z = (k / 2) ^ (1/4) + // the total value of the pool when x = y is 2 * z, which is 2 * (k / 2) ^ (1/4) + const zSquared = sqrt(k.mul(precision).div(2)); + const z = sqrt(zSquared.mul(precision)); + return z.mul(2); + }; + + const calcInvariant = (reserves) => { + const x = reserves.ws; + const y = reserves.os; + const a = x.mul(y).div(precision); + const b = x.mul(x).div(precision).add(y.mul(y).div(precision)); + const k = a.mul(b).div(precision); + + log(`Invariant: ${formatUnits(k)}`); + + return k; + }; + + // Babylonian square root function for Ethers.js BigNumber + function sqrt(value) { + // Convert input to BigNumber if it isn't already + let bn = ethers.BigNumber.from(value); + + // Handle edge cases + if (bn.lt(0)) { + throw new Error("Square root of negative number is not supported"); + } + if (bn.eq(0)) { + return ethers.BigNumber.from(0); + } + + // Initial guess (number / 2) + let guess = bn.div(2); + + // Define precision threshold (in wei scale, 10^-18) + const epsilon = ethers.BigNumber.from("1"); // 1 wei precision + + // Keep refining until we reach desired precision + while (true) { + // Babylonian method: nextGuess = (guess + number/guess) / 2 + // Using mul and div for BigNumber arithmetic + let numerator = guess.add(bn.div(guess)); + let nextGuess = numerator.div(2); + + // Calculate absolute difference + let diff = nextGuess.gt(guess) + ? nextGuess.sub(guess) + : guess.sub(nextGuess); + + // If difference is less than epsilon, we're done + if (diff.lte(epsilon)) { + return nextGuess; + } + + // Update guess for next iteration + guess = nextGuess; + } + } + + const snapData = async () => { + const { oSonicVault, swapXAMOStrategy, oSonic, swapXPool, swapXGauge, wS } = + fixture; + + const stratBalance = await swapXAMOStrategy.checkBalance(wS.address); + const osSupply = await oSonic.totalSupply(); + const vaultAssets = await oSonicVault.totalValue(); + const poolSupply = await swapXPool.totalSupply(); + const { _reserve0: wsReserves, _reserve1: osReserves } = + await swapXPool.getReserves(); + const stratGaugeBalance = await swapXGauge.balanceOf( + swapXAMOStrategy.address + ); + const gaugeSupply = await swapXGauge.totalSupply(); + const vaultWSBalance = await wS.balanceOf(oSonicVault.address); + const stratWSBalance = await wS.balanceOf(swapXAMOStrategy.address); + + return { + stratBalance, + osSupply, + vaultAssets, + poolSupply, + reserves: { ws: wsReserves, os: osReserves }, + stratGaugeBalance, + gaugeSupply, + vaultWSBalance, + stratWSBalance, + }; + }; + + const logSnapData = async (data, message) => { + const totalReserves = data.reserves.ws.add(data.reserves.os); + const reserversPercentage = { + ws: data.reserves.ws.mul(10000).div(totalReserves), + os: data.reserves.os.mul(10000).div(totalReserves), + }; + if (message) { + log(message); + } + log(`Strategy balance : ${formatUnits(data.stratBalance)}`); + log(`OS supply : ${formatUnits(data.osSupply)}`); + log(`Vault assets : ${formatUnits(data.vaultAssets)}`); + log(`pool supply : ${formatUnits(data.poolSupply)}`); + log( + `reserves wS : ${formatUnits(data.reserves.ws)} ${formatUnits( + reserversPercentage.ws, + 2 + )}%` + ); + log( + `reserves OS : ${formatUnits(data.reserves.os)} ${formatUnits( + reserversPercentage.os, + 2 + )}%` + ); + log(`strat gauge balance : ${formatUnits(data.stratGaugeBalance)}`); + log(`gauge supply : ${formatUnits(data.gaugeSupply)}`); + log(`vault wS balance : ${formatUnits(data.vaultWSBalance)}`); + log(`strat wS balance : ${formatUnits(data.stratWSBalance)}`); + }; + + const logProfit = async (dataBefore) => { + const { oSonic, oSonicVault } = fixture; + + const osSupplyAfter = await oSonic.totalSupply(); + const vaultAssetsAfter = await oSonicVault.totalValue(); + const profit = vaultAssetsAfter + .sub(dataBefore.vaultAssets) + .add(dataBefore.osSupply.sub(osSupplyAfter)); + log( + `Change vault assets : ${formatUnits( + vaultAssetsAfter.sub(dataBefore.vaultAssets) + )}` + ); + log( + `Change OS supply : ${formatUnits( + dataBefore.osSupply.sub(osSupplyAfter) + )}` + ); + log(`Profit : ${formatUnits(profit)}`); + + return profit; + }; + + const assertChangedData = async (dataBefore, delta) => { + const { oSonic, oSonicVault, swapXAMOStrategy, swapXPool, swapXGauge, wS } = + fixture; + + if (delta.stratBalance != undefined) { + const expectedStratBalance = dataBefore.stratBalance.add( + delta.stratBalance + ); + log(`Expected strategy balance: ${formatUnits(expectedStratBalance)}`); + expect(await swapXAMOStrategy.checkBalance(wS.address)).to.withinRange( + expectedStratBalance.sub(3), + expectedStratBalance.add(3), + "Strategy's check balance" + ); + } + + if (delta.osSupply != undefined) { + const expectedSupply = dataBefore.osSupply.add(delta.osSupply); + expect(await oSonic.totalSupply(), "OSonic total supply").to.equal( + expectedSupply + ); + } + + // Check Vault's wS balance + if (delta.vaultWSBalance != undefined) { + expect(await wS.balanceOf(oSonicVault.address)).to.equal( + dataBefore.vaultWSBalance.add(delta.vaultWSBalance), + "Vault's wS balance" + ); + } + + // Check the pool's reserves + if (delta.reserves != undefined) { + const { _reserve0: wsReserves, _reserve1: osReserves } = + await swapXPool.getReserves(); + + // If the wS reserves delta is a function, call it to check the wS reserves + if (typeof delta.reserves.ws == "function") { + // Call test function to check the wS reserves + delta.reserves.ws(wsReserves); + } else { + expect(wsReserves, "wS reserves").to.equal( + dataBefore.reserves.ws.add(delta.reserves.ws) + ); + } + // Check OS reserves delta + expect(osReserves, "OS reserves").to.equal( + dataBefore.reserves.os.add(delta.reserves.os) + ); + } + + if (delta.stratGaugeBalance) { + // Check the strategy's gauge balance + const expectedStratGaugeBalance = dataBefore.stratGaugeBalance.add( + delta.stratGaugeBalance + ); + expect( + await swapXGauge.balanceOf(swapXAMOStrategy.address) + ).to.withinRange( + expectedStratGaugeBalance.sub(1), + expectedStratGaugeBalance.add(1), + "Strategy's gauge balance" + ); + } + }; + + async function assertDeposit(wsDepositAmount) { + const { + clement, + swapXAMOStrategy, + oSonic, + oSonicVault, + swapXPool, + oSonicVaultSigner, + wS, + } = fixture; + + await oSonicVault.connect(clement).mint(wS.address, wsDepositAmount, 0); + + const dataBefore = await snapData(); + await logSnapData(dataBefore, "\nBefore depositing wS to strategy"); + + const { lpMintAmount, osMintAmount } = await calcOSMintAmount( + wsDepositAmount + ); + + // Vault transfers wS to strategy + await wS + .connect(oSonicVaultSigner) + .transfer(swapXAMOStrategy.address, wsDepositAmount); + // Vault calls deposit on the strategy + const tx = await swapXAMOStrategy + .connect(oSonicVaultSigner) + .deposit(wS.address, wsDepositAmount); + + await logSnapData( + await snapData(), + `\nAfter depositing ${formatUnits(wsDepositAmount)} wS to strategy` + ); + await logProfit(dataBefore); + + // Check emitted events + await expect(tx) + .to.emit(swapXAMOStrategy, "Deposit") + .withArgs(wS.address, swapXPool.address, wsDepositAmount); + await expect(tx) + .to.emit(swapXAMOStrategy, "Deposit") + .withArgs(oSonic.address, swapXPool.address, osMintAmount); + + // Calculate the value of the wS and OS tokens added to the pool if the pool was balanced + const depositValue = calcReserveValue({ + ws: wsDepositAmount, + os: osMintAmount, + }); + // log(`Value of deposit: ${formatUnits(depositValue)}`); + + await assertChangedData(dataBefore, { + stratBalance: depositValue, + osSupply: osMintAmount, + reserves: { ws: wsDepositAmount, os: osMintAmount }, + vaultWSBalance: wsDepositAmount.mul(-1), + gaugeSupply: lpMintAmount, + }); + } + + async function assertWithdrawAll() { + const { swapXAMOStrategy, swapXPool, oSonic, oSonicVaultSigner, wS } = + fixture; + + const dataBefore = await snapData(); + await logSnapData(dataBefore); + + const { osBurnAmount, wsWithdrawAmount } = await calcWithdrawAllAmounts(); + + // Now try to withdraw all the wS from the strategy + const tx = await swapXAMOStrategy.connect(oSonicVaultSigner).withdrawAll(); + + await logSnapData(await snapData(), "\nAfter full withdraw"); + await logProfit(dataBefore); + + // Check emitted events + await expect(tx) + .to.emit(swapXAMOStrategy, "Withdrawal") + .withArgs(wS.address, swapXPool.address, wsWithdrawAmount); + await expect(tx) + .to.emit(swapXAMOStrategy, "Withdrawal") + .withArgs(oSonic.address, swapXPool.address, osBurnAmount); + + // Calculate the value of the wS and OS tokens removed from the pool if the pool was balanced + const withdrawValue = calcReserveValue({ + ws: wsWithdrawAmount, + os: osBurnAmount, + }); + + await assertChangedData(dataBefore, { + stratBalance: withdrawValue.mul(-1), + osSupply: osBurnAmount.mul(-1), + reserves: { ws: wsWithdrawAmount.mul(-1), os: osBurnAmount.mul(-1) }, + vaultWSBalance: wsWithdrawAmount, + stratGaugeBalance: dataBefore.gaugeSupply.mul(-1), + }); + } + + async function assertWithdrawPartial(wsWithdrawAmount) { + const { + swapXAMOStrategy, + oSonic, + swapXPool, + oSonicVault, + oSonicVaultSigner, + wS, + } = fixture; + + const dataBefore = await snapData(); + + const { lpBurnAmount, osBurnAmount } = await calcOSWithdrawAmount( + wsWithdrawAmount + ); + + // Now try to withdraw the wS from the strategy + const tx = await swapXAMOStrategy + .connect(oSonicVaultSigner) + .withdraw(oSonicVault.address, wS.address, wsWithdrawAmount); + + await logSnapData( + await snapData(), + `\nAfter withdraw of ${formatUnits(wsWithdrawAmount)}` + ); + await logProfit(dataBefore); + + // Check emitted events + await expect(tx) + .to.emit(swapXAMOStrategy, "Withdrawal") + .withArgs(wS.address, swapXPool.address, wsWithdrawAmount); + await expect(tx).to.emit(swapXAMOStrategy, "Withdrawal").withNamedArgs({ + _asset: oSonic.address, + _pToken: swapXPool.address, + }); + + // Calculate the value of the wS and OS tokens removed from the pool if the pool was balanced + const withdrawValue = calcReserveValue({ + ws: wsWithdrawAmount, + os: osBurnAmount, + }); + + await assertChangedData(dataBefore, { + stratBalance: withdrawValue.mul(-1), + osSupply: osBurnAmount.mul(-1), + reserves: { + ws: (actualWsReserve) => { + const expectedWsReserves = + dataBefore.reserves.ws.sub(wsWithdrawAmount); + + expect(actualWsReserve).to.withinRange( + expectedWsReserves.sub(2), + expectedWsReserves, + "wS reserves" + ); + }, + os: osBurnAmount.mul(-1), + }, + vaultWSBalance: wsWithdrawAmount, + gaugeSupply: lpBurnAmount.mul(-1), + }); + } + + async function assertSwapAssetsToPool(wsAmount) { + const { swapXAMOStrategy, swapXPool, strategist, wS } = fixture; + + const dataBefore = await snapData(); + await logSnapData(dataBefore, "Before swapping assets to the pool"); + + const { lpBurnAmount: expectedLpBurnAmount, osBurnAmount: osBurnAmount1 } = + await calcOSWithdrawAmount(wsAmount); + // TODO this is not accurate as the liquidity needs to be removed first + const osBurnAmount2 = await swapXPool.getAmountOut(wsAmount, wS.address); + const osBurnAmount = osBurnAmount1.add(osBurnAmount2); + + // Swap wS to the pool and burn the received OS from the pool + const tx = await swapXAMOStrategy + .connect(strategist) + .swapAssetsToPool(wsAmount); + + await logSnapData(await snapData(), "\nAfter swapping assets to the pool"); + await logProfit(dataBefore); + + // Check emitted event + await expect(tx).to.emittedEvent("SwapAssetsToPool", [ + (actualWsAmount) => { + expect(actualWsAmount).to.withinRange( + wsAmount.sub(1), + wsAmount.add(1), + "SwapAssetsToPool event wsAmount" + ); + }, + expectedLpBurnAmount, + (actualOsBurnAmount) => { + // TODO this can be tightened once osBurnAmount is more accurately calculated + expect(actualOsBurnAmount).to.approxEqualTolerance( + osBurnAmount, + 10, + "SwapAssetsToPool event osBurnt" + ); + }, + ]); + + await assertChangedData( + dataBefore, + { + // stratBalance: osBurnAmount.mul(-1), + vaultWSBalance: 0, + stratGaugeBalance: 0, + }, + fixture + ); + } + + async function assertSwapOTokensToPool(osAmount) { + const { swapXAMOStrategy, strategist } = fixture; + + const dataBefore = await snapData(); + + // Mint OS and swap into the pool, then mint more OS to add with the wS swapped out + const tx = await swapXAMOStrategy + .connect(strategist) + .swapOTokensToPool(osAmount); + + // Check emitted event + await expect(tx) + .emit(swapXAMOStrategy, "SwapOTokensToPool") + .withNamedArgs({ osMinted: osAmount }); + + await logProfit(dataBefore); + + await assertChangedData( + dataBefore, + { + // stratBalance: osBurnAmount.mul(-1), + vaultWSBalance: 0, + stratGaugeBalance: 0, + }, + fixture + ); + } + + // Calculate the minted OS amount for a deposit + async function calcOSMintAmount(wsDepositAmount) { + const { swapXPool } = fixture; + + // Get the reserves of the pool + const { _reserve0: wsReserves, _reserve1: osReserves } = + await swapXPool.getReserves(); + + const osMintAmount = wsDepositAmount.mul(osReserves).div(wsReserves); + log(`OS mint amount : ${formatUnits(osMintAmount)}`); + + const lpTotalSupply = await swapXPool.totalSupply(); + const lpMintAmount = wsDepositAmount.mul(lpTotalSupply).div(wsReserves); + + return { lpMintAmount, osMintAmount }; + } + + // Calculate the amount of OS burnt from a withdraw + async function calcOSWithdrawAmount(wsWithdrawAmount) { + const { swapXPool } = fixture; + + // Get the reserves of the pool + const { _reserve0: wsReserves, _reserve1: osReserves } = + await swapXPool.getReserves(); + + // lp tokens to burn = wS withdrawn * total LP supply / wS pool balance + const totalLpSupply = await swapXPool.totalSupply(); + const lpBurnAmount = wsWithdrawAmount + .mul(totalLpSupply) + .div(wsReserves) + .add(1); + // OS to burn = LP tokens to burn * OS reserves / total LP supply + const osBurnAmount = lpBurnAmount.mul(osReserves).div(totalLpSupply); + + log(`OS burn amount : ${formatUnits(osBurnAmount)}`); + + return { lpBurnAmount, osBurnAmount }; + } + + // Calculate the OS and wS amounts from a withdrawAll + async function calcWithdrawAllAmounts() { + const { swapXAMOStrategy, swapXGauge, swapXPool } = fixture; + + // Get the reserves of the pool + const { _reserve0: wsReserves, _reserve1: osReserves } = + await swapXPool.getReserves(); + const strategyLpAmount = await swapXGauge.balanceOf( + swapXAMOStrategy.address + ); + const totalLpSupply = await swapXPool.totalSupply(); + + // wS to withdraw = wS pool balance * strategy LP amount / total pool LP amount + const wsWithdrawAmount = wsReserves + .mul(strategyLpAmount) + .div(totalLpSupply); + // OS to burn = OS pool balance * strategy LP amount / total pool LP amount + const osBurnAmount = osReserves.mul(strategyLpAmount).div(totalLpSupply); + + log(`wS withdraw amount : ${formatUnits(wsWithdrawAmount)}`); + log(`OS burn amount : ${formatUnits(osBurnAmount)}`); + + return { + wsWithdrawAmount, + osBurnAmount, + }; + } +}); diff --git a/contracts/test/vault/oeth-vault.js b/contracts/test/vault/oeth-vault.js index 35a3eb9913..d8b432a6d4 100644 --- a/contracts/test/vault/oeth-vault.js +++ b/contracts/test/vault/oeth-vault.js @@ -335,15 +335,14 @@ describe("OETH Vault", function () { it("Fail to cacheWETHAssetIndex if WETH is not a supported asset", async () => { const { frxETH, weth } = fixture; - const { deployerAddr } = await hre.getNamedAccounts(); - const sDeployer = hre.ethers.provider.getSigner(deployerAddr); await deployWithConfirmation("MockOETHVault", [weth.address]); const mockVault = await hre.ethers.getContract("MockOETHVault"); await mockVault.supportAsset(frxETH.address); - const tx = mockVault.connect(sDeployer).cacheWETHAssetIndex(); + const mockGovernor = await impersonateAndFund(addresses.dead); + const tx = mockVault.connect(mockGovernor).cacheWETHAssetIndex(); await expect(tx).to.be.revertedWith("Invalid WETH Asset Index"); }); @@ -441,7 +440,10 @@ describe("OETH Vault", function () { it("Should allow strategy to burnForStrategy", async () => { const { oethVault, oeth, weth, governor, daniel } = fixture; - await oethVault.connect(governor).setOusdMetaStrategy(daniel.address); + await oethVault.connect(governor).approveStrategy(daniel.address); + await oethVault + .connect(governor) + .addStrategyToMintWhitelist(daniel.address); // First increase netOusdMintForStrategyThreshold await oethVault @@ -463,26 +465,21 @@ describe("OETH Vault", function () { ); }); - it("Fail when burnForStrategy because Amoount too high", async () => { + it("Fail when burnForStrategy because amount > int256 ", async () => { const { oethVault, governor, daniel } = fixture; - await oethVault.connect(governor).setOusdMetaStrategy(daniel.address); + await oethVault.connect(governor).approveStrategy(daniel.address); + await oethVault + .connect(governor) + .addStrategyToMintWhitelist(daniel.address); + const tx = oethVault .connect(daniel) .burnForStrategy(parseUnits("10", 76)); - await expect(tx).to.be.revertedWith("Amount too high"); - }); - - it("Fail when burnForStrategy because Attempting to burn too much OUSD.", async () => { - const { oethVault, governor, daniel } = fixture; - - await oethVault.connect(governor).setOusdMetaStrategy(daniel.address); - - // Then try to burn more than authorized - const tx = oethVault.connect(daniel).burnForStrategy(oethUnits("0")); - - await expect(tx).to.be.revertedWith("Attempting to burn too much OUSD."); + await expect(tx).to.be.revertedWith( + "SafeCast: value doesn't fit in an int256" + ); }); }); diff --git a/contracts/test/vault/vault.mainnet.fork-test.js b/contracts/test/vault/vault.mainnet.fork-test.js index c96e0be31e..ecca27c19b 100644 --- a/contracts/test/vault/vault.mainnet.fork-test.js +++ b/contracts/test/vault/vault.mainnet.fork-test.js @@ -13,6 +13,9 @@ const { isCI, } = require("./../helpers"); const { impersonateAndFund } = require("../../utils/signers"); +const { + shouldHaveRewardTokensConfigured, +} = require("./../behaviour/reward-tokens.fork"); const log = require("../../utils/logger")("test:fork:ousd:vault"); @@ -389,41 +392,20 @@ describe("ForkTest: Vault", function () { }); }); - // We no longer have any strategies that harvest these reward tokens - // shouldHaveRewardTokensConfigured(() => ({ - // vault: fixture.vault, - // harvester: fixture.harvester, - // expectedConfigs: { - // [fixture.aave.address]: { - // allowedSlippageBps: 300, - // harvestRewardBps: 100, - // swapPlatformAddr: "0xE592427A0AEce92De3Edee1F18E0157C05861564", - // doSwapRewardToken: true, - // swapPlatform: 1, - // liquidationLimit: 0, - // uniswapV3Path: - // "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9002710c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4dac17f958d2ee523a2206206994597c13d831ec7", - // }, - // [fixture.cvx.address]: { - // allowedSlippageBps: 300, - // harvestRewardBps: 100, - // swapPlatformAddr: "0xE592427A0AEce92De3Edee1F18E0157C05861564", - // doSwapRewardToken: true, - // swapPlatform: 1, - // liquidationLimit: ousdUnits("2500"), - // uniswapV3Path: - // "0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b002710c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4dac17f958d2ee523a2206206994597c13d831ec7", - // }, - // [fixture.crv.address]: { - // allowedSlippageBps: 300, - // harvestRewardBps: 200, - // swapPlatformAddr: "0xE592427A0AEce92De3Edee1F18E0157C05861564", - // doSwapRewardToken: true, - // swapPlatform: 1, - // liquidationLimit: ousdUnits("4000"), - // uniswapV3Path: - // "0xd533a949740bb3306d119cc777fa900ba034cd52000bb8c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4dac17f958d2ee523a2206206994597c13d831ec7", - // }, - // }, - // })); + shouldHaveRewardTokensConfigured(() => ({ + vault: fixture.vault, + harvester: fixture.harvester, + expectedConfigs: { + [fixture.aave.address]: { + allowedSlippageBps: 300, + harvestRewardBps: 100, + swapPlatformAddr: "0xE592427A0AEce92De3Edee1F18E0157C05861564", + doSwapRewardToken: true, + swapPlatform: 1, + liquidationLimit: 0, + uniswapV3Path: + "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9002710c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f4dac17f958d2ee523a2206206994597c13d831ec7", + }, + }, + })); }); diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index 05498d404e..2226378f97 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -383,6 +383,9 @@ addresses.sonic.WOSonicProxy = "0x9F0dF7799f6FDAd409300080cfF680f5A23df4b1"; addresses.sonic.OSonicVaultProxy = "0xa3c0eCA00D2B76b4d1F170b0AB3FdeA16C180186"; // SwapX on Sonic +addresses.sonic.SWPx = "0xA04BC7140c26fc9BB1F36B1A604C7A5a88fb0E70"; +addresses.sonic.SwapXOwner = "0xAdB5A1518713095C39dBcA08Da6656af7249Dd20"; +addresses.sonic.SwapXVoter = "0xC1AE2779903cfB84CB9DEe5c03EcEAc32dc407F2"; addresses.sonic.SwapXSWPxOSPool = "0x9Cb484FAD38D953bc79e2a39bBc93655256F0B16"; addresses.sonic.SwapXTreasury = "0x896c3f0b63a8DAE60aFCE7Bca73356A9b611f3c8"; addresses.sonic.SwapXOsUSDCe = {}; @@ -401,6 +404,10 @@ addresses.sonic.SwapXOsGEMSx = {}; addresses.sonic.SwapXOsGEMSx.pool = "0x9ac7F5961a452e9cD5Be5717bD2c3dF412D1c1a5"; +addresses.sonic.SwapXWSOS = {}; +addresses.sonic.SwapXWSOS.pool = "0xcfE67b6c7B65c8d038e666b3241a161888B7f2b0"; +addresses.sonic.SwapXWSOS.gauge = "0x083D761B2A3e1fb5914FA61c6Bf11A93dcb60709"; + addresses.sonic.SwapXOsUSDCeMultisigBooster = "0x4636269e7CDc253F6B0B210215C3601558FE80F6"; addresses.sonic.SwapXOsGEMSxMultisigBooster = @@ -474,9 +481,10 @@ addresses.sonic.Shadow.OsEco.yf_treasury = "0x4b9919603170c77936d8ec2c08b604844e861699"; // Sonic Curve +addresses.sonic.CRV = "0x5Af79133999f7908953E94b7A5CF367740Ebee35"; addresses.sonic.WS_OS = {}; -addresses.sonic.WS_OS.pool = "0x7180f41a71f13fac52d2cfb17911f5810c8b0bb9"; -addresses.sonic.WS_OS.gauge = "0x9ca6de419e9fc7bac876de07f0f6ec96331ba207"; +addresses.sonic.WS_OS.pool = "0x7180F41A71f13FaC52d2CfB17911f5810c8B0BB9"; +addresses.sonic.WS_OS.gauge = "0x9CA6dE419e9fc7bAC876DE07F0f6Ec96331Ba207"; addresses.sonic.childLiquidityGaugeFactory = "0xf3A431008396df8A8b2DF492C913706BDB0874ef"; diff --git a/contracts/utils/deploy.js b/contracts/utils/deploy.js index 189738b8d0..432e1f4085 100644 --- a/contracts/utils/deploy.js +++ b/contracts/utils/deploy.js @@ -138,7 +138,7 @@ const withConfirmation = async ( ? process.env.HOLESKY_PROVIDER_URL : process.env.PROVIDER_URL; if (providerUrl?.includes("rpc.tenderly.co") || (isTest && !isForkTest)) { - console.log("Skipping confirmation on Tenderly or for unit tests"); + log("Skipping confirmation on Tenderly or for unit tests"); // Skip on Tenderly and for unit tests return result; }