From cd4310255b3ef672723ff9f75051d93e14d4754c Mon Sep 17 00:00:00 2001 From: transferAndCall Date: Sun, 10 Oct 2021 08:02:01 -0500 Subject: [PATCH 1/2] Refactor to 1 vault = 1 token Signed-off-by: transferAndCall --- contracts/v3/Harvester.sol | 9 +- contracts/v3/Manager.sol | 36 +-- contracts/v3/Vault.sol | 86 ++------ contracts/v3/VaultHelper.sol | 51 +---- contracts/v3/controllers/LegacyController.sol | 48 ++-- contracts/v3/interfaces/ILegacyController.sol | 2 +- contracts/v3/interfaces/IManager.sol | 3 +- contracts/v3/interfaces/IVault.sol | 12 +- .../{3.1.VaultStables.js => 3.1.Vault3CRV.js} | 41 +--- deploy/v3/5.1.VaultStablesConverter.js | 2 +- deploy/v3/6.1.NativeStrategyCurve3Crv.js | 2 +- deploy/v3/6.2.ConvexStrategy3Crv.js | 2 +- deploy/v3/6.3.MIMConvexStrategy.js | 2 +- deploy/v3/99.Test.js | 41 +++- test/v3/Controller.test.js | 31 +-- test/v3/Gauges.test.js | 64 +++--- test/v3/Harvester.test.js | 32 +-- test/v3/LegacyController.test.js | 12 +- test/v3/Manager.test.js | 45 +--- test/v3/StablesConverter.test.js | 2 +- test/v3/Vault.test.js | 206 ++---------------- test/v3/VaultHelper.test.js | 92 +++----- 22 files changed, 232 insertions(+), 589 deletions(-) rename deploy/v3/{3.1.VaultStables.js => 3.1.Vault3CRV.js} (57%) diff --git a/contracts/v3/Harvester.sol b/contracts/v3/Harvester.sol index aa5a32e7..82f57ce3 100644 --- a/contracts/v3/Harvester.sol +++ b/contracts/v3/Harvester.sol @@ -167,13 +167,12 @@ contract Harvester is IHarvester { function earn( address _strategy, - IVault _vault, - address _token + address _vault ) external onlyHarvester { - _vault.earn(_token, _strategy); + IVault(_vault).earn(_strategy); } /** @@ -225,17 +224,15 @@ contract Harvester is IHarvester { /** * @notice Earns tokens in the LegacyController to the v3 vault - * @param _token The address of the token * @param _expected The expected amount to deposit after conversion */ function legacyEarn( - address _token, uint256 _expected ) external onlyHarvester { - legacyController.legacyDeposit(_token, _expected); + legacyController.legacyDeposit(_expected); } /** diff --git a/contracts/v3/Manager.sol b/contracts/v3/Manager.sol index 64f550df..85132e1b 100644 --- a/contracts/v3/Manager.sol +++ b/contracts/v3/Manager.sol @@ -61,8 +61,8 @@ contract Manager is IManager { // vault => controller mapping(address => address) public override controllers; - // vault => tokens[] - mapping(address => address[]) public override tokens; + // vault => token + mapping(address => address) internal tokens; // token => vault mapping(address => address) public override vaults; @@ -429,10 +429,10 @@ contract Manager is IManager { { require(allowedTokens[_token], "!allowedTokens"); require(allowedVaults[_vault], "!allowedVaults"); - require(tokens[_vault].length < MAX_TOKENS, ">tokens"); + require(tokens[_vault] == address(0), "!_vault"); require(vaults[_token] == address(0), "!_token"); vaults[_token] = _vault; - tokens[_vault].push(_token); + tokens[_vault] = _token; emit TokenAdded(_vault, _token); } @@ -469,25 +469,11 @@ contract Manager is IManager { notHalted onlyStrategist { - uint256 k = tokens[_vault].length; - uint256 index; - bool found; - - for (uint i = 0; i < k; i++) { - if (tokens[_vault][i] == _token) { - index = i; - found = true; - break; - } - } - - // TODO: Verify added check - if (found) { - tokens[_vault][index] = tokens[_vault][k-1]; - tokens[_vault].pop(); - delete vaults[_token]; - emit TokenRemoved(_vault, _token); - } + require(tokens[_vault] == _token, "!_token"); + require(vaults[_token] == _vault, "!_vault"); + delete tokens[_vault]; + delete vaults[_token]; + emit TokenRemoved(_vault, _token); } /** @@ -530,13 +516,13 @@ contract Manager is IManager { * @notice Returns an array of token addresses for a given vault * @param _vault The address of the vault */ - function getTokens( + function getToken( address _vault ) external view override - returns (address[] memory) + returns (address) { return tokens[_vault]; } diff --git a/contracts/v3/Vault.sol b/contracts/v3/Vault.sol index 2bef06ae..ff15feef 100644 --- a/contracts/v3/Vault.sol +++ b/contracts/v3/Vault.sol @@ -108,29 +108,6 @@ contract Vault is VaultToken, IVault { totalDepositCap = _totalDepositCap; } - /** - * @notice Swaps tokens held within the vault - * @param _token0 The token address to swap out - * @param _token1 The token address to to - * @param _expectedAmount The expected amount of _token1 to receive - */ - function swap( - address _token0, - address _token1, - uint256 _expectedAmount - ) - external - override - notHalted - onlyStrategist - returns (uint256 _balance) - { - IConverter _converter = IConverter(IController(manager.controllers(address(this))).converter(address(this))); - _balance = IERC20(_token0).balanceOf(address(this)); - IERC20(_token0).safeTransfer(address(_converter), _balance); - _balance = _converter.convert(_token0, _token1, _balance, _expectedAmount); - } - /** * HARVESTER-ONLY FUNCTIONS */ @@ -139,16 +116,15 @@ contract Vault is VaultToken, IVault { * @notice Sends accrued 3CRV tokens on the metavault to the controller to be deposited to strategies */ function earn( - address _token, address _strategy ) external override - checkToken(_token) notHalted onlyHarvester { require(manager.allowedStrategies(_strategy), "!_strategy"); + address _token = getToken(); IController _controller = IController(manager.controllers(address(this))); if (_controller.investEnabled()) { uint256 _balance = available(_token); @@ -164,20 +140,18 @@ contract Vault is VaultToken, IVault { /** * @notice Deposits the given token into the vault - * @param _token The address of the token * @param _amount The amount of tokens to deposit */ function deposit( - address _token, uint256 _amount ) public override - checkToken(_token) notHalted returns (uint256 _shares) { require(_amount > 0, "!_amount"); + address _token = getToken(); uint256 _balance = balance(); @@ -202,40 +176,17 @@ contract Vault is VaultToken, IVault { } } - /** - * @notice Deposits multiple tokens simultaneously to the vault - * @dev Users must approve the vault to spend their stablecoin - * @param _tokens The addresses of each token being deposited - * @param _amounts The amounts of each token being deposited - */ - function depositMultiple( - address[] calldata _tokens, - uint256[] calldata _amounts - ) - external - override - returns (uint256 _shares) - { - require(_tokens.length == _amounts.length, "!length"); - - for (uint8 i; i < _amounts.length; i++) { - _shares = _shares.add(deposit(_tokens[i], _amounts[i])); - } - } - /** * @notice Withdraws an amount of shares to a given output token * @param _shares The amount of shares to withdraw - * @param _output The address of the token to receive */ function withdraw( - uint256 _shares, - address _output + uint256 _shares ) public override - checkToken(_output) { + address _output = getToken(); uint256 _amount = (balance().mul(_shares)).div(totalSupply()); _burn(msg.sender, _shares); @@ -265,15 +216,12 @@ contract Vault is VaultToken, IVault { /** * @notice Withdraw the entire balance for an account - * @param _output The address of the desired token to receive */ - function withdrawAll( - address _output - ) + function withdrawAll() external override { - withdraw(balanceOf(msg.sender), _output); + withdraw(balanceOf(msg.sender)); } /** @@ -315,13 +263,10 @@ contract Vault is VaultToken, IVault { function balanceOfThis() public view - returns (uint256 _balance) + returns (uint256) { - address[] memory _tokens = manager.getTokens(address(this)); - for (uint8 i; i < _tokens.length; i++) { - address _token = _tokens[i]; - _balance = _balance.add(_normalizeDecimals(_token, IERC20(_token).balanceOf(address(this)))); - } + address _token = getToken(); + return _normalizeDecimals(_token, IERC20(_token).balanceOf(address(this))); } /** @@ -343,13 +288,13 @@ contract Vault is VaultToken, IVault { /** * @notice Returns an array of the tokens for this vault */ - function getTokens() - external + function getToken() + public view override - returns (address[] memory) + returns (address) { - return manager.getTokens(address(this)); + return manager.getToken(address(this)); } /** @@ -386,11 +331,6 @@ contract Vault is VaultToken, IVault { * MODIFIERS */ - modifier checkToken(address _token) { - require(manager.allowedTokens(_token) && manager.vaults(_token) == address(this), "!_token"); - _; - } - modifier notHalted() { require(!manager.halted(), "halted"); _; diff --git a/contracts/v3/VaultHelper.sol b/contracts/v3/VaultHelper.sol index 060e4981..fd5484e9 100644 --- a/contracts/v3/VaultHelper.sol +++ b/contracts/v3/VaultHelper.sol @@ -19,58 +19,23 @@ contract VaultHelper { using SafeERC20 for IERC20; /** - * @notice Deposits the given token into the specified vault + * @notice Deposits into the specified vault and stakes in the gauge * @dev Users must approve the vault helper to spend their token * @param _vault The address of the vault - * @param _token The address of the token * @param _amount The amount of tokens to deposit */ function depositVault( address _vault, - address _token, uint256 _amount ) external { require(_amount > 0, "!_amount"); + address _token = IVault(_vault).getToken(); IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount); IERC20(_token).safeApprove(_vault, 0); IERC20(_token).safeApprove(_vault, _amount); - uint256 _shares = IVault(_vault).deposit(_token, _amount); - address _gauge = IVault(_vault).gauge(); - if (_gauge != address(0)) { - IERC20(_vault).safeApprove(_gauge, 0); - IERC20(_vault).safeApprove(_gauge, _shares); - ILiquidityGaugeV2(_gauge).deposit(_shares); - IERC20(_gauge).safeTransfer(msg.sender, _shares); - } else { - IERC20(_vault).safeTransfer(msg.sender, _shares); - } - } - - /** - * @notice Deposits multiple tokens simultaneously to the specified vault - * @dev Users must approve the vault helper to spend their tokens - * @param _vault The address of the vault - * @param _tokens The addresses of each token being deposited - * @param _amounts The amounts of each token being deposited - */ - function depositMultipleVault( - address _vault, - address[] calldata _tokens, - uint256[] calldata _amounts - ) - external - { - require(_tokens.length == _amounts.length, "!length"); - - for (uint8 i = 0; i < _amounts.length; i++) { - require(_amounts[i] > 0, "!_amounts"); - IERC20(_tokens[i]).safeTransferFrom(msg.sender, address(this), _amounts[i]); - IERC20(_tokens[i]).safeApprove(_vault, 0); - IERC20(_tokens[i]).safeApprove(_vault, _amounts[i]); - } - uint256 _shares = IVault(_vault).depositMultiple(_tokens, _amounts); + uint256 _shares = IVault(_vault).deposit(_amount); address _gauge = IVault(_vault).gauge(); if (_gauge != address(0)) { IERC20(_vault).safeApprove(_gauge, 0); @@ -84,21 +49,21 @@ contract VaultHelper { function withdrawVault( address _vault, - address _toToken, uint256 _amount ) external { address _gauge = IVault(_vault).gauge(); + address _token = IVault(_vault).getToken(); if (_gauge != address(0)) { IERC20(_gauge).safeTransferFrom(msg.sender, address(this), _amount); ILiquidityGaugeV2(_gauge).withdraw(_amount); - IVault(_vault).withdraw(IERC20(_vault).balanceOf(address(this)), _toToken); - IERC20(_toToken).safeTransfer(msg.sender, IERC20(_toToken).balanceOf(address(this))); + IVault(_vault).withdraw(IERC20(_vault).balanceOf(address(this))); + IERC20(_token).safeTransfer(msg.sender, IERC20(_token).balanceOf(address(this))); } else { IERC20(_vault).safeTransferFrom(msg.sender, address(this), _amount); - IVault(_vault).withdraw(_amount, _toToken); - IERC20(_toToken).safeTransfer(msg.sender, IERC20(_toToken).balanceOf(address(this))); + IVault(_vault).withdraw(_amount); + IERC20(_token).safeTransfer(msg.sender, IERC20(_token).balanceOf(address(this))); } } } diff --git a/contracts/v3/controllers/LegacyController.sol b/contracts/v3/controllers/LegacyController.sol index 074f2b4c..74e9b3c3 100644 --- a/contracts/v3/controllers/LegacyController.sol +++ b/contracts/v3/controllers/LegacyController.sol @@ -58,7 +58,7 @@ contract LegacyController is ILegacyController { { IVault cachedVault = vault; if (address(cachedVault) != address(0)) { - cachedVault.withdrawAll(address(token)); + cachedVault.withdrawAll(); token.safeTransfer(metavault, token.balanceOf(address(this))); } vault = IVault(_vault); @@ -109,14 +109,12 @@ contract LegacyController is ILegacyController { /** * @notice Returns the balance of the given token on the vault - * @param _token The address of the token */ function balanceOf( - address _token + address ) external view - onlyToken(_token) returns (uint256) { return token.balanceOf(address(this)) @@ -125,16 +123,14 @@ contract LegacyController is ILegacyController { /** * @notice Returns the withdraw fee for withdrawing the given token and amount - * @param _token The address of the token * @param _amount The amount to withdraw */ function withdrawFee( - address _token, + address , uint256 _amount ) external view - onlyToken(_token) returns (uint256) { return manager.withdrawalProtectionFee().mul(_amount).div(MAX); @@ -161,16 +157,15 @@ contract LegacyController is ILegacyController { uint256 _toWithdraw = _amount.sub(_balance); IVault cachedVault = vault; // convert to vault shares - address[] memory _tokens = cachedVault.getTokens(); - require(_tokens.length > 0, "!_tokens"); + address _token = cachedVault.getToken(); // get the amount of the token that we would be withdrawing - uint256 _expected = converter.expected(address(token), _tokens[0], _toWithdraw); + uint256 _expected = converter.expected(address(token), _token, _toWithdraw); uint256 _shares = _expected.mul(1e18).div(cachedVault.getPricePerFullShare()); - cachedVault.withdraw(_shares, _tokens[0]); - _balance = IERC20(_tokens[0]).balanceOf(address(this)); - IERC20(_tokens[0]).safeTransfer(address(converter), _balance); + cachedVault.withdraw(_shares); + _balance = IERC20(_token).balanceOf(address(this)); + IERC20(_token).safeTransfer(address(converter), _balance); // TODO: calculate expected - converter.convert(_tokens[0], address(token), _balance, 1); + converter.convert(_token, address(token), _balance, 1); emit Withdraw(token.balanceOf(address(this))); token.safeTransfer(metavault, token.balanceOf(address(this))); } @@ -193,11 +188,9 @@ contract LegacyController is ILegacyController { /** * @notice Deposits the given token to the v3 vault - * @param _toToken The address to convert to * @param _expected The expected amount to deposit after conversion */ function legacyDeposit( - address _toToken, uint256 _expected ) external @@ -205,14 +198,13 @@ contract LegacyController is ILegacyController { onlyEnabledConverter onlyHarvester { - if (_toToken != address(token)) { - uint256 _amount = token.balanceOf(address(this)); - token.safeTransfer(address(converter), _amount); - converter.convert(address(token), _toToken, _amount, _expected); - } - IERC20(_toToken).safeApprove(address(vault), 0); - IERC20(_toToken).safeApprove(address(vault), type(uint256).max); - vault.deposit(_toToken, IERC20(_toToken).balanceOf(address(this))); + address _token = vault.getToken(); + uint256 _amount = token.balanceOf(address(this)); + token.safeTransfer(address(converter), _amount); + converter.convert(address(token), _token, _amount, _expected); + IERC20(_token).safeApprove(address(vault), 0); + IERC20(_token).safeApprove(address(vault), type(uint256).max); + vault.deposit(IERC20(_token).balanceOf(address(this))); } /** @@ -254,12 +246,4 @@ contract LegacyController is ILegacyController { require(msg.sender == manager.strategist(), "!strategist"); _; } - - /** - * @notice Reverts if the given token is not the stored token - */ - modifier onlyToken(address _token) { - require(_token == address(token), "!_token"); - _; - } } diff --git a/contracts/v3/interfaces/ILegacyController.sol b/contracts/v3/interfaces/ILegacyController.sol index 3f440997..ec264ac0 100644 --- a/contracts/v3/interfaces/ILegacyController.sol +++ b/contracts/v3/interfaces/ILegacyController.sol @@ -3,5 +3,5 @@ pragma solidity ^0.6.0; interface ILegacyController { - function legacyDeposit(address _token, uint256 _expected) external; + function legacyDeposit(uint256 _expected) external; } diff --git a/contracts/v3/interfaces/IManager.sol b/contracts/v3/interfaces/IManager.sol index 1c1bc87c..9264a3d7 100644 --- a/contracts/v3/interfaces/IManager.sol +++ b/contracts/v3/interfaces/IManager.sol @@ -11,7 +11,7 @@ interface IManager { function allowedVaults(address) external view returns (bool); function controllers(address) external view returns (address); function getHarvestFeeInfo() external view returns (address, address, uint256); - function getTokens(address) external view returns (address[] memory); + function getToken(address) external view returns (address); function governance() external view returns (address); function halted() external view returns (bool); function harvester() external view returns (address); @@ -23,7 +23,6 @@ interface IManager { function stakingPool() external view returns (address); function stakingPoolShareFee() external view returns (uint256); function strategist() external view returns (address); - function tokens(address, uint256) external view returns (address); function treasury() external view returns (address); function treasuryFee() external view returns (uint256); function vaults(address) external view returns (address); diff --git a/contracts/v3/interfaces/IVault.sol b/contracts/v3/interfaces/IVault.sol index 2f8bded0..f0de146e 100644 --- a/contracts/v3/interfaces/IVault.sol +++ b/contracts/v3/interfaces/IVault.sol @@ -7,15 +7,13 @@ import "./IManager.sol"; interface IVault { function available(address _token) external view returns (uint256); function balance() external view returns (uint256); - function deposit(address _token, uint256 _amount) external returns (uint256); - function depositMultiple(address[] calldata _tokens, uint256[] calldata _amount) external returns (uint256); - function earn(address _token, address _strategy) external; + function deposit(uint256 _amount) external returns (uint256); + function earn(address _strategy) external; function gauge() external returns (address); function getPricePerFullShare() external view returns (uint256); - function getTokens() external view returns (address[] memory); + function getToken() external view returns (address); function manager() external view returns (IManager); - function swap(address _token0, address _token1, uint256 _expectedAmount) external returns (uint256); - function withdraw(uint256 _amount, address _output) external; - function withdrawAll(address _output) external; + function withdraw(uint256 _amount) external; + function withdrawAll() external; function withdrawFee(uint256 _amount) external view returns (uint256); } diff --git a/deploy/v3/3.1.VaultStables.js b/deploy/v3/3.1.Vault3CRV.js similarity index 57% rename from deploy/v3/3.1.VaultStables.js rename to deploy/v3/3.1.Vault3CRV.js index 9d072d94..6cb208cb 100644 --- a/deploy/v3/3.1.VaultStables.js +++ b/deploy/v3/3.1.Vault3CRV.js @@ -1,29 +1,25 @@ module.exports = async ({ getChainId, getNamedAccounts, deployments }) => { const { deploy, execute } = deployments; const chainId = await getChainId(); - let { DAI, deployer, USDC, USDT } = await getNamedAccounts(); + let { deployer, T3CRV } = await getNamedAccounts(); const Controller = await deployments.get('Controller'); const Manager = await deployments.get('Manager'); const Minter = await deployments.get('Minter'); const GaugeProxy = await deployments.get('GaugeProxy'); if (chainId != '1') { - const dai = await deployments.get('DAI'); - const usdc = await deployments.get('USDC'); - const usdt = await deployments.get('USDT'); - DAI = dai.address; - USDC = usdc.address; - USDT = usdt.address; + const t3crv = await deployments.get('T3CRV'); + T3CRV = t3crv.address; } - const Vault = await deploy('VaultStables', { + const Vault = await deploy('Vault3CRV', { contract: 'Vault', from: deployer, log: true, - args: ['yAxis Stablecoin Canonical Vault', 'CV:S', Manager.address] + args: ['yAxis 3CRV Canonical Vault', 'CV:3CRV', Manager.address] }); - const Gauge = await deploy('VaultStablesGauge', { + const Gauge = await deploy('Vault3CRVGauge', { contract: 'LiquidityGaugeV2', from: deployer, log: true, @@ -46,29 +42,19 @@ module.exports = async ({ getChainId, getNamedAccounts, deployments }) => { Vault.address, true ); - await execute('Manager', { from: deployer, log: true }, 'setAllowedToken', DAI, true); - await execute('Manager', { from: deployer, log: true }, 'setAllowedToken', USDC, true); - await execute('Manager', { from: deployer, log: true }, 'setAllowedToken', USDT, true); await execute( 'Manager', { from: deployer, log: true }, - 'addToken', - Vault.address, - DAI - ); - await execute( - 'Manager', - { from: deployer, log: true }, - 'addToken', - Vault.address, - USDC + 'setAllowedToken', + T3CRV, + true ); await execute( 'Manager', { from: deployer, log: true }, 'addToken', Vault.address, - USDT + T3CRV ); await execute( 'Manager', @@ -77,12 +63,7 @@ module.exports = async ({ getChainId, getNamedAccounts, deployments }) => { Vault.address, Controller.address ); - await execute( - 'VaultStables', - { from: deployer, log: true }, - 'setGauge', - Gauge.address - ); + await execute('Vault3CRV', { from: deployer, log: true }, 'setGauge', Gauge.address); } }; diff --git a/deploy/v3/5.1.VaultStablesConverter.js b/deploy/v3/5.1.VaultStablesConverter.js index 29c0cc31..afeaf493 100644 --- a/deploy/v3/5.1.VaultStablesConverter.js +++ b/deploy/v3/5.1.VaultStablesConverter.js @@ -4,7 +4,7 @@ module.exports = async ({ getNamedAccounts, deployments, getChainId }) => { let { DAI, USDC, USDT, T3CRV, deployer, stableSwap3Pool } = await getNamedAccounts(); const chainId = await getChainId(); const Manager = await deployments.get('Manager'); - const Vault = await deployments.get('VaultStables'); + const Vault = await deployments.get('Vault3CRV'); if (chainId != '1') { const dai = await deployments.get('DAI'); diff --git a/deploy/v3/6.1.NativeStrategyCurve3Crv.js b/deploy/v3/6.1.NativeStrategyCurve3Crv.js index 885cb26a..e9620b00 100644 --- a/deploy/v3/6.1.NativeStrategyCurve3Crv.js +++ b/deploy/v3/6.1.NativeStrategyCurve3Crv.js @@ -17,7 +17,7 @@ module.exports = async ({ getNamedAccounts, deployments, getChainId }) => { const chainId = await getChainId(); const Controller = await deployments.get('Controller'); const Manager = await deployments.get('Manager'); - const Vault = await deployments.get('VaultStables'); + const Vault = await deployments.get('Vault3CRV'); const name = 'Curve: 3CRV'; if (chainId != '1') { diff --git a/deploy/v3/6.2.ConvexStrategy3Crv.js b/deploy/v3/6.2.ConvexStrategy3Crv.js index e203c41b..cd457c9f 100644 --- a/deploy/v3/6.2.ConvexStrategy3Crv.js +++ b/deploy/v3/6.2.ConvexStrategy3Crv.js @@ -17,7 +17,7 @@ module.exports = async ({ getNamedAccounts, deployments, getChainId }) => { const chainId = await getChainId(); const Controller = await deployments.get('Controller'); const Manager = await deployments.get('Manager'); - const Vault = await deployments.get('VaultStables'); + const Vault = await deployments.get('Vault3CRV'); const name = 'Convex: 3CRV'; let pid = 9; diff --git a/deploy/v3/6.3.MIMConvexStrategy.js b/deploy/v3/6.3.MIMConvexStrategy.js index e5b9f10c..1c7e7caa 100644 --- a/deploy/v3/6.3.MIMConvexStrategy.js +++ b/deploy/v3/6.3.MIMConvexStrategy.js @@ -16,7 +16,7 @@ module.exports = async ({ getNamedAccounts, deployments, getChainId }) => { const chainId = await getChainId(); const Controller = await deployments.get('Controller'); const Manager = await deployments.get('Manager'); - const Vault = await deployments.get('VaultStables'); + const Vault = await deployments.get('Vault3CRV'); const name = 'Convex: MIMCRV'; let pid = 9; diff --git a/deploy/v3/99.Test.js b/deploy/v3/99.Test.js index 2c981617..7584a00c 100644 --- a/deploy/v3/99.Test.js +++ b/deploy/v3/99.Test.js @@ -210,7 +210,7 @@ module.exports = async ({ getNamedAccounts, deployments }) => { from: deployer, args: [T3CRV.address] }); - const Minter = await deployments.deploy('MockCurveMinter', { + const CRVMinter = await deployments.deploy('MockCurveMinter', { from: deployer, args: [CRV.address] }); @@ -218,11 +218,46 @@ module.exports = async ({ getNamedAccounts, deployments }) => { from: deployer, args: ['0x0000000000000000000000000000000000000000'] }); - await deployments.deploy('VaultStables', { + const Vault = await deployments.deploy('VaultStables', { contract: 'Vault', from: deployer, args: ['Vault: Stables', 'MV:S', Manager.address] }); + const MinterWrapper = await deployments.deploy('MinterWrapper', { + from: deployer, + log: true, + args: [YAXIS.address] + }); + const VotingEscrow = await deployments.deploy('VotingEscrow', { + from: deployer, + log: true, + args: [YAXIS.address, 'Vote-escrowed YAXIS', 'veYAXIS', 'veYAXIS_1.0.0'] + }); + const GaugeController = await deployments.deploy('GaugeController', { + from: deployer, + log: true, + args: [YAXIS.address, VotingEscrow.address] + }); + const Minter = await deployments.deploy('Minter', { + from: deployer, + log: true, + args: [MinterWrapper.address, GaugeController.address] + }); + const GaugeProxy = await deployments.deploy('GaugeProxy', { + from: deployer, + log: true, + args: [deployer, deployer] + }); + await deployments.deploy('VaultStablesGauge', { + contract: 'LiquidityGaugeV2', + from: deployer, + log: true, + args: [Vault.address, Minter.address, GaugeProxy.address] + }); + await deployments.deploy('VaultHelper', { + from: deployer, + log: true + }); await deployments.deploy('NativeStrategyCurve3Crv', { from: deployer, args: [ @@ -234,7 +269,7 @@ module.exports = async ({ getNamedAccounts, deployments }) => { USDC.address, USDT.address, Gauge.address, - Minter.address, + CRVMinter.address, StableSwap.address, Controller.address, Manager.address, diff --git a/test/v3/Controller.test.js b/test/v3/Controller.test.js index 38d4bdee..b742d749 100644 --- a/test/v3/Controller.test.js +++ b/test/v3/Controller.test.js @@ -376,10 +376,8 @@ describe('Controller', () => { await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000); - await harvester - .connect(deployer) - .earn(strategyCrv.address, vault.address, dai.address); + await vault.connect(user).deposit(1000); + await harvester.connect(deployer).earn(strategyCrv.address, vault.address); }); it('should set the cap and withdraw excess tokens', async () => { @@ -579,8 +577,8 @@ describe('Controller', () => { await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000); - await harvester.earn(strategyCrv.address, vault.address, dai.address); + await vault.connect(user).deposit(1000); + await harvester.earn(strategyCrv.address, vault.address); }); it('should withdraw tokens sent directly', async () => { @@ -631,8 +629,8 @@ describe('Controller', () => { await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000000); - await harvester.earn(strategyCrv.address, vault.address, dai.address); + await vault.connect(user).deposit(1000000); + await harvester.earn(strategyCrv.address, vault.address); await crv.faucet(ether('1000')); await weth.faucet(ether('2000')); @@ -679,16 +677,11 @@ describe('Controller', () => { await manager.connect(deployer).setAllowedToken(dai.address, true); await manager.connect(deployer).setAllowedToken(t3crv.address, true); await manager.addToken(vault.address, dai.address); - await manager.addToken(vault.address, t3crv.address); await manager.setController(vault.address, controller.address); await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000); - - await t3crv.connect(user).faucet(1000); - await t3crv.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(t3crv.address, 1000); + await vault.connect(user).deposit(1000); }); it('should revert if called by an address other than the vault', async () => { @@ -729,21 +722,19 @@ describe('Controller', () => { await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000); + await vault.connect(user).deposit(1000); }); it('should withdraw from vault', async () => { expect(await dai.balanceOf(user.address)).to.be.equal(0); - await vault.connect(user).withdraw(1000, dai.address); + await vault.connect(user).withdraw(1000); expect(await dai.balanceOf(user.address)).to.be.at.least(999); }); it('should withdraw from strategies', async () => { - await harvester - .connect(deployer) - .earn(strategyCrv.address, vault.address, dai.address); + await harvester.connect(deployer).earn(strategyCrv.address, vault.address); expect(await dai.balanceOf(user.address)).to.be.equal(0); - await vault.connect(user).withdraw(1000, dai.address); + await vault.connect(user).withdraw(1000); expect(await dai.balanceOf(user.address)).to.be.at.least(998); }); diff --git a/test/v3/Gauges.test.js b/test/v3/Gauges.test.js index 9ac7017b..b893ec58 100644 --- a/test/v3/Gauges.test.js +++ b/test/v3/Gauges.test.js @@ -12,14 +12,14 @@ describe('Gauges', () => { const MAXTIME = 1 * 365 * 86400; let deployer, treasury, user; let controller, - dai, + t3crv, gaugeController, gaugeProxy, manager, minter, minterWrapper, - vaultStables, - vaultStablesGauge, + vault3Crv, + vault3CrvGauge, votingEscrow, yaxis; @@ -32,8 +32,8 @@ describe('Gauges', () => { controller = await ethers.getContractAt('Controller', Controller.address); const YAXIS = await deployments.get('YaxisToken'); yaxis = await ethers.getContractAt('YaxisToken', YAXIS.address); - const DAI = await deployments.get('DAI'); - dai = await ethers.getContractAt('MockERC20', DAI.address); + const T3CRV = await deployments.get('T3CRV'); + t3crv = await ethers.getContractAt('MockERC20', T3CRV.address); const GaugeController = await deployments.get('GaugeController'); gaugeController = await ethers.getContractAt( 'GaugeController', @@ -48,12 +48,12 @@ describe('Gauges', () => { minterWrapper = await ethers.getContractAt('MinterWrapper', MinterWrapper.address); const Minter = await deployments.get('Minter'); minter = await ethers.getContractAt('Minter', Minter.address); - const VaultStables = await deployments.get('VaultStables'); - vaultStables = await ethers.getContractAt('Vault', VaultStables.address); - const VaultStablesGauge = await deployments.get('VaultStablesGauge'); - vaultStablesGauge = await ethers.getContractAt( + const Vault3CRV = await deployments.get('Vault3CRV'); + vault3Crv = await ethers.getContractAt('Vault', Vault3CRV.address); + const Vault3CRVGauge = await deployments.get('Vault3CRVGauge'); + vault3CrvGauge = await ethers.getContractAt( 'LiquidityGaugeV2', - VaultStablesGauge.address + Vault3CRVGauge.address ); }); @@ -67,12 +67,12 @@ describe('Gauges', () => { expect(await minter.token()).to.be.equal(minterWrapper.address); expect(await minter.controller()).to.be.equal(gaugeController.address); expect(await minterWrapper.token()).to.be.equal(yaxis.address); - expect(await vaultStablesGauge.crv_token()).to.be.equal(minterWrapper.address); - expect(await vaultStablesGauge.lp_token()).to.be.equal(vaultStables.address); - expect(await vaultStablesGauge.controller()).to.be.equal(gaugeController.address); - expect(await vaultStablesGauge.admin()).to.be.equal(gaugeProxy.address); - expect(await vaultStablesGauge.minter()).to.be.equal(minter.address); - expect(await vaultStables.getPricePerFullShare()).to.equal(0); + expect(await vault3CrvGauge.crv_token()).to.be.equal(minterWrapper.address); + expect(await vault3CrvGauge.lp_token()).to.be.equal(vault3Crv.address); + expect(await vault3CrvGauge.controller()).to.be.equal(gaugeController.address); + expect(await vault3CrvGauge.admin()).to.be.equal(gaugeProxy.address); + expect(await vault3CrvGauge.minter()).to.be.equal(minter.address); + expect(await vault3Crv.getPricePerFullShare()).to.equal(0); }); it('should fund the minterWrapper with YAXIS', async () => { @@ -93,11 +93,11 @@ describe('Gauges', () => { }); it('should allow users to vote for a gauge', async () => { - expect(await gaugeController.get_gauge_weight(vaultStablesGauge.address)).to.be.equal( + expect(await gaugeController.get_gauge_weight(vault3CrvGauge.address)).to.be.equal( ether('1') ); - await gaugeController.vote_for_gauge_weights(vaultStablesGauge.address, 10000); - expect(await gaugeController.get_gauge_weight(vaultStablesGauge.address)).to.be.above( + await gaugeController.vote_for_gauge_weights(vault3CrvGauge.address, 10000); + expect(await gaugeController.get_gauge_weight(vault3CrvGauge.address)).to.be.above( ether('0.97') ); }); @@ -105,22 +105,20 @@ describe('Gauges', () => { it('should allow users to stake vault tokens in a gauge', async () => { await increaseTime(86400 * 7); await manager.connect(deployer).setAllowedController(controller.address, true); - await manager.connect(deployer).setAllowedVault(vaultStables.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager - .connect(deployer) - .setController(vaultStables.address, controller.address); - await dai.connect(user).faucet(ether('1000')); - await dai.connect(user).approve(vaultStables.address, ethers.constants.MaxUint256); - await vaultStables.connect(user).deposit(dai.address, ether('1000')); - expect(await vaultStables.balanceOf(user.address)).to.be.equal(ether('1000')); - await vaultStables + await manager.connect(deployer).setAllowedVault(vault3Crv.address, true); + await manager.connect(deployer).setAllowedToken(t3crv.address, true); + await manager.connect(deployer).setController(vault3Crv.address, controller.address); + await t3crv.connect(user).faucet(ether('1000')); + await t3crv.connect(user).approve(vault3Crv.address, ethers.constants.MaxUint256); + await vault3Crv.connect(user).deposit(ether('1000')); + expect(await vault3Crv.balanceOf(user.address)).to.be.equal(ether('1000')); + await vault3Crv .connect(user) - .approve(vaultStablesGauge.address, ethers.constants.MaxUint256); - expect(await vaultStablesGauge.balanceOf(user.address)).to.be.equal(0); - await vaultStablesGauge + .approve(vault3CrvGauge.address, ethers.constants.MaxUint256); + expect(await vault3CrvGauge.balanceOf(user.address)).to.be.equal(0); + await vault3CrvGauge .connect(user) ['deposit(uint256,address)'](ether('1000'), user.address); - expect(await vaultStablesGauge.balanceOf(user.address)).to.be.equal(ether('1000')); + expect(await vault3CrvGauge.balanceOf(user.address)).to.be.equal(ether('1000')); }); }); diff --git a/test/v3/Harvester.test.js b/test/v3/Harvester.test.js index 21aecaff..21f4111e 100644 --- a/test/v3/Harvester.test.js +++ b/test/v3/Harvester.test.js @@ -173,17 +173,17 @@ describe('Harvester', () => { await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000); + await vault.connect(user).deposit(1000); }); it('should revert when called by an address other than the harvester', async () => { await expect( - harvester.connect(user).earn(strategyCrv.address, vault.address, dai.address) + harvester.connect(user).earn(strategyCrv.address, vault.address) ).to.be.revertedWith('!harvester'); }); it('should pass when called by the harvester', async () => { - await harvester.earn(strategyCrv.address, vault.address, dai.address); + await harvester.earn(strategyCrv.address, vault.address); }); }); @@ -192,12 +192,12 @@ describe('Harvester', () => { await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000); - await harvester.earn(strategyCrv.address, vault.address, dai.address); + await vault.connect(user).deposit(1000); + await harvester.earn(strategyCrv.address, vault.address); await dai.connect(user).faucet(1000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000); - await harvester.earn(strategyCrv.address, vault.address, dai.address); + await vault.connect(user).deposit(1000); + await harvester.earn(strategyCrv.address, vault.address); await crv.faucet(ether('1000')); await weth.faucet(ether('2000')); await dai.faucet(ether('1000')); @@ -224,12 +224,12 @@ describe('Harvester', () => { await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000); - await harvester.earn(strategyCrv.address, vault.address, dai.address); + await vault.connect(user).deposit(1000); + await harvester.earn(strategyCrv.address, vault.address); await dai.connect(user).faucet(1000); await dai.connect(user).approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(user).deposit(dai.address, 1000); - await harvester.earn(strategyCrv.address, vault.address, dai.address); + await vault.connect(user).deposit(1000); + await harvester.earn(strategyCrv.address, vault.address); await crv.faucet(ether('1000')); await weth.faucet(ether('2000')); await dai.faucet(ether('1000')); @@ -261,7 +261,7 @@ describe('Harvester', () => { }); }); - describe('earn', () => { + describe('legacyEarn', () => { beforeEach(async () => { await t3crv.connect(user).faucet(ether('100000')); await t3crv.connect(user).transfer(legacyController.address, ether('1000')); @@ -271,13 +271,13 @@ describe('Harvester', () => { }); it('should revert when called by an address other than the harvester', async () => { - await expect( - harvester.connect(user).legacyEarn(t3crv.address, 0) - ).to.be.revertedWith('!harvester'); + await expect(harvester.connect(user).legacyEarn(0)).to.be.revertedWith( + '!harvester' + ); }); it('should pass when called by the harvester', async () => { - await harvester.connect(deployer).legacyEarn(dai.address, 0); + await harvester.connect(deployer).legacyEarn(0); }); }); }); diff --git a/test/v3/LegacyController.test.js b/test/v3/LegacyController.test.js index be72535c..fe0dc4c5 100644 --- a/test/v3/LegacyController.test.js +++ b/test/v3/LegacyController.test.js @@ -123,12 +123,6 @@ describe('LegacyController', () => { await legacyController.withdrawFee(t3crv.address, ether('1000')) ).to.be.equal(ether('1')); }); - - it('should revert if given token is not 3CRV', async () => { - await expect( - legacyController.withdrawFee(ethers.constants.AddressZero, 1) - ).to.be.revertedWith('!_token'); - }); }); describe('earn', () => { @@ -171,11 +165,9 @@ describe('LegacyController', () => { describe('when withdrawing from a strategy', () => { beforeEach(async () => { expect(await strategy.balanceOf()).to.be.equal(0); - await harvester.connect(deployer).legacyEarn(dai.address, 1); + await harvester.connect(deployer).legacyEarn(1); await expect( - harvester - .connect(deployer) - .earn(strategy.address, vault.address, dai.address) + harvester.connect(deployer).earn(strategy.address, vault.address) ).to.emit(vault, 'Earn'); expect(await strategy.balanceOf()).to.be.above(ether('900')); }); diff --git a/test/v3/Manager.test.js b/test/v3/Manager.test.js index 34bd52c1..d7dbbe22 100644 --- a/test/v3/Manager.test.js +++ b/test/v3/Manager.test.js @@ -743,31 +743,7 @@ describe('Manager', () => { .withArgs(vault.address, dai.address); await expect( manager.connect(deployer).addToken(vault.address, dai.address) - ).to.be.revertedWith('!_token'); - }); - - it('should revert when it reaches the maximum number of tokens in vault', async () => { - const _numToAddress = (num) => `0x${(num + 1).toString().padStart(40, '0')}`; - - // Fill manager with maximum number of tokens in a vault - const max = 256; - const addTokens = new Array(max) - .fill(0) - .map((_, i) => - manager.connect(treasury).setAllowedToken(_numToAddress(i), true) - ); - await Promise.all(addTokens); - const addMax = new Array(max) - .fill(0) - .map((v, i) => - manager.connect(deployer).addToken(vault.address, _numToAddress(i)) - ); - await Promise.all(addMax); - - // The next should fail - await expect( - manager.connect(deployer).addToken(vault.address, dai.address) - ).to.be.revertedWith('>tokens'); + ).to.be.revertedWith('!_vault'); }); }); @@ -832,25 +808,12 @@ describe('Manager', () => { await expect(manager.connect(deployer).removeToken(vault.address, dai.address)) .to.emit(manager, 'TokenRemoved') .withArgs(vault.address, dai.address); - expect((await manager.getTokens(vault.address)).length).to.equal(0); }); - it('should do nothing if vault is not added', async () => { + it('should revert if the token is not added', async () => { await expect( - manager - .connect(deployer) - .removeToken(ethers.constants.AddressZero, dai.address) - ).to.not.emit(manager, 'TokenRemoved'); - expect((await manager.getTokens(vault.address)).length).to.equal(1); - }); - - it('should do nothing if token is not added', async () => { - await expect( - manager - .connect(deployer) - .removeToken(vault.address, ethers.constants.AddressZero) - ).to.not.emit(manager, 'TokenRemoved'); - expect((await manager.getTokens(vault.address)).length).to.equal(1); + manager.connect(deployer).removeToken(vault.address, usdc.address) + ).to.be.revertedWith('!_token'); }); }); diff --git a/test/v3/StablesConverter.test.js b/test/v3/StablesConverter.test.js index 02c52676..0aede7fa 100644 --- a/test/v3/StablesConverter.test.js +++ b/test/v3/StablesConverter.test.js @@ -12,7 +12,7 @@ describe('StablesConverter', () => { let converter, dai, t3crv, usdc, usdt, stableSwap3Pool, manager; beforeEach(async () => { - await deployments.fixture(['v3', 'NativeStrategyCurve3Crv']); + await deployments.fixture('test'); [deployer, , , user] = await ethers.getSigners(); const Manager = await deployments.get('Manager'); manager = await ethers.getContractAt('Manager', Manager.address); diff --git a/test/v3/Vault.test.js b/test/v3/Vault.test.js index 7ff03b1c..fc8a4208 100644 --- a/test/v3/Vault.test.js +++ b/test/v3/Vault.test.js @@ -9,19 +9,13 @@ const ether = parseEther; describe('Vault', () => { let deployer, treasury, user; - let dai, t3crv, usdc, usdt, vault, manager, controller, harvester, strategyCrv, converter; + let dai, vault, manager, controller, harvester, strategyCrv, converter; beforeEach(async () => { await deployments.fixture('test'); [deployer, treasury, , user] = await ethers.getSigners(); const DAI = await deployments.get('DAI'); dai = await ethers.getContractAt('MockERC20', DAI.address); - const USDC = await deployments.get('USDC'); - usdc = await ethers.getContractAt('MockERC20', USDC.address); - const USDT = await deployments.get('USDT'); - usdt = await ethers.getContractAt('MockERC20', USDT.address); - const T3CRV = await deployments.get('T3CRV'); - t3crv = await ethers.getContractAt('MockERC20', T3CRV.address); const Manager = await deployments.get('Manager'); manager = await ethers.getContractAt('Manager', Manager.address); const Harvester = await deployments.get('Harvester'); @@ -41,12 +35,7 @@ describe('Vault', () => { await manager.setAllowedVault(vault.address, true); await manager.setGovernance(treasury.address); await dai.connect(user).faucet(ether('100000001')); - await usdc.connect(user).faucet('100000000000000'); - await usdt.connect(user).faucet('100000000000000'); await dai.connect(user).approve(Vault.address, ethers.constants.MaxUint256); - await usdc.connect(user).approve(Vault.address, ethers.constants.MaxUint256); - await usdt.connect(user).approve(Vault.address, ethers.constants.MaxUint256); - await t3crv.connect(user).approve(Vault.address, ethers.constants.MaxUint256); }); it('should deploy with expected state', async () => { @@ -125,46 +114,26 @@ describe('Vault', () => { it('should revert when called by an address other than the harvester', async () => { await expect( - vault.connect(user).earn(dai.address, ethers.constants.AddressZero) + vault.connect(user).earn(ethers.constants.AddressZero) ).to.be.revertedWith('!harvester'); }); - it('should revert when the token is not added', async () => { - await expect( - harvester - .connect(deployer) - .earn( - ethers.constants.AddressZero, - vault.address, - ethers.constants.AddressZero - ) - ).to.be.revertedWith('!_token'); - }); - it('should revert when halted', async () => { await manager.setHalted(); await expect( - harvester - .connect(deployer) - .earn(ethers.constants.AddressZero, vault.address, dai.address) + harvester.connect(deployer).earn(ethers.constants.AddressZero, vault.address) ).to.be.revertedWith('halted'); }); it('should revert if strategy isnt allowed', async () => { await expect( - harvester - .connect(deployer) - .earn(ethers.constants.AddressZero, vault.address, dai.address) + harvester.connect(deployer).earn(ethers.constants.AddressZero, vault.address) ).to.be.revertedWith('!_strategy'); }); it('should earn when strategy is added', async () => { expect(await dai.balanceOf(vault.address)).to.equal(1000); - await expect( - harvester - .connect(deployer) - .earn(strategyCrv.address, vault.address, dai.address) - ) + await expect(harvester.connect(deployer).earn(strategyCrv.address, vault.address)) .to.emit(vault, 'Earn') .withArgs(dai.address, 950); expect(await dai.balanceOf(vault.address)).to.equal(50); @@ -174,9 +143,7 @@ describe('Vault', () => { expect(await dai.balanceOf(vault.address)).to.equal(1000); await controller.setInvestEnabled(false); await expect( - harvester - .connect(deployer) - .earn(strategyCrv.address, vault.address, dai.address) + harvester.connect(deployer).earn(strategyCrv.address, vault.address) ).to.not.emit(vault, 'Earn'); expect(await dai.balanceOf(vault.address)).to.equal(1000); }); @@ -190,112 +157,43 @@ describe('Vault', () => { }); const newVault = await ethers.getContractAt('Vault', NewVault.address); await dai.connect(user).approve(NewVault.address, ethers.utils.parseEther('1000')); - await expect(newVault.connect(user).deposit(dai.address, 1)).to.be.revertedWith( - '!_token' - ); + await expect(newVault.connect(user).deposit(1)).to.be.reverted; }); context('when the vault is set up', () => { beforeEach(async () => { await manager.connect(treasury).setAllowedToken(dai.address, true); - await manager.connect(treasury).setAllowedToken(usdc.address, true); await manager.connect(treasury).setAllowedVault(vault.address, true); await manager.connect(treasury).setAllowedController(controller.address, true); await manager.setController(vault.address, controller.address); await expect(manager.addToken(vault.address, dai.address)) .to.emit(manager, 'TokenAdded') .withArgs(vault.address, dai.address); - await expect(manager.addToken(vault.address, usdc.address)) - .to.emit(manager, 'TokenAdded') - .withArgs(vault.address, usdc.address); - expect((await vault.getTokens()).length).to.equal(2); - expect(await manager.tokens(vault.address, 0)).to.equal(dai.address); - expect(await manager.tokens(vault.address, 1)).to.equal(usdc.address); - }); - - it('should revert when a token is not added', async () => { - await expect( - vault.connect(user).depositMultiple([dai.address, usdt.address], [1, 1]) - ).to.be.revertedWith('!_token'); }); it('should revert when halted', async () => { await manager.setHalted(); - await expect( - vault.connect(user).deposit(dai.address, ether('1000')) - ).to.be.revertedWith('halted'); + await expect(vault.connect(user).deposit(ether('1000'))).to.be.revertedWith( + 'halted' + ); }); it('should revert if deposit amount is 0', async () => { - await expect(vault.connect(user).deposit(dai.address, 0)).to.be.revertedWith( - '!_amount' - ); + await expect(vault.connect(user).deposit(0)).to.be.revertedWith('!_amount'); }); it('should revert if the deposit amount is greater than the total deposit cap', async () => { - await expect(vault.connect(user).deposit(dai.address, ether('10000001'))).to.be - .reverted; - }); - - it('should revert if the input lengths do not match', async () => { - await expect( - vault.connect(user).depositMultiple([dai.address], [ether('1000'), 1]) - ).to.be.revertedWith('!length'); + await expect(vault.connect(user).deposit(ether('10000001'))).to.be.reverted; }); it('should deposit single token', async () => { expect(await vault.balanceOf(user.address)).to.equal(0); - await expect(vault.connect(user).deposit(dai.address, ether('1000'))) + await expect(vault.connect(user).deposit(ether('1000'))) .to.emit(vault, 'Deposit') .withArgs(user.address, ether('1000')); expect(await vault.balanceOf(user.address)).to.equal(ether('1000')); expect(await vault.totalSupply()).to.equal(ether('1000')); }); - - it('should deposit multiple tokens', async () => { - expect(await vault.balanceOf(user.address)).to.equal(0); - await expect( - vault - .connect(user) - .depositMultiple( - [dai.address, usdc.address], - [ether('1000'), '1000000000'] - ) - ) - // Deposit is actually emitted multiple times - .to.emit(vault, 'Deposit'); - expect(await vault.balanceOf(user.address)).to.equal(ether('2000')); - expect(await vault.totalSupply()).to.equal(ether('2000')); - }); - - context('when depositing multiple tokens multiple times', () => { - beforeEach(async () => { - expect(await vault.balanceOf(user.address)).to.equal(0); - await expect( - vault - .connect(user) - .depositMultiple( - [dai.address, usdc.address], - [ether('1000'), '1000000000'] - ) - ).to.emit(vault, 'Deposit'); - expect(await vault.balanceOf(user.address)).to.equal(ether('2000')); - expect(await vault.totalSupply()).to.equal(ether('2000')); - }); - - it('should grant additional shares', async () => { - await expect( - vault - .connect(user) - .depositMultiple( - [dai.address, usdc.address], - [ether('1000'), '1000000000'] - ) - ).to.emit(vault, 'Deposit'); - expect(await vault.balanceOf(user.address)).to.equal(ether('4000')); - expect(await vault.totalSupply()).to.equal(ether('4000')); - }); - }); }); }); @@ -308,40 +206,33 @@ describe('Vault', () => { await expect(manager.connect(deployer).addToken(vault.address, dai.address)) .to.emit(manager, 'TokenAdded') .withArgs(vault.address, dai.address); - expect((await vault.getTokens()).length).to.equal(1); - }); - - it('should revert if the output token is not added', async () => { - await expect(vault.withdraw(0, usdc.address)).to.be.revertedWith('!_token'); }); it('should revert if there are no deposits', async () => { - await expect(vault.withdraw(1, dai.address)).to.be.revertedWith( - 'SafeMath: division by zero' - ); + await expect(vault.withdraw(1)).to.be.revertedWith('SafeMath: division by zero'); }); context('when users have deposited', () => { beforeEach(async () => { - await expect(vault.connect(user).deposit(dai.address, ether('1000'))) + await expect(vault.connect(user).deposit(ether('1000'))) .to.emit(vault, 'Deposit') .withArgs(user.address, ether('1000')); }); it('should revert if withdrawing more than the balance', async () => { - await expect( - vault.connect(user).withdraw(ether('1001'), dai.address) - ).to.be.revertedWith('ERC20: burn amount exceeds balance'); + await expect(vault.connect(user).withdraw(ether('1001'))).to.be.revertedWith( + 'ERC20: burn amount exceeds balance' + ); }); it('should withdraw partial amounts', async () => { - await expect(vault.connect(user).withdraw(ether('100'), dai.address)) + await expect(vault.connect(user).withdraw(ether('100'))) .to.emit(vault, 'Withdraw') .withArgs(user.address, ether('99.9')); }); it('should withdraw the full amount', async () => { - await expect(vault.connect(user).withdraw(ether('1000'), dai.address)) + await expect(vault.connect(user).withdraw(ether('1000'))) .to.emit(vault, 'Withdraw') .withArgs(user.address, ether('999')); }); @@ -349,7 +240,7 @@ describe('Vault', () => { it('should withdraw the full amount even if tokens are sent directly', async () => { await dai.connect(user).faucet(ether('1')); await dai.connect(user).transfer(vault.address, ether('1')); - await expect(vault.connect(user).withdraw(ether('1000'), dai.address)) + await expect(vault.connect(user).withdraw(ether('1000'))) .to.emit(vault, 'Withdraw') .withArgs(user.address, ether('999.999')); }); @@ -365,30 +256,23 @@ describe('Vault', () => { await expect(manager.connect(deployer).addToken(vault.address, dai.address)) .to.emit(manager, 'TokenAdded') .withArgs(vault.address, dai.address); - expect((await vault.getTokens()).length).to.equal(1); - }); - - it('should revert if the output token is not added', async () => { - await expect(vault.connect(user).withdrawAll(usdc.address)).to.be.revertedWith( - '!_token' - ); }); it('should revert if there are no deposits', async () => { - await expect(vault.connect(user).withdrawAll(dai.address)).to.be.revertedWith( + await expect(vault.connect(user).withdrawAll()).to.be.revertedWith( 'SafeMath: division by zero' ); }); context('when users have deposited', () => { beforeEach(async () => { - await expect(vault.connect(user).deposit(dai.address, ether('1000'))) + await expect(vault.connect(user).deposit(ether('1000'))) .to.emit(vault, 'Deposit') .withArgs(user.address, ether('1000')); }); it('should withdraw the full amount', async () => { - await expect(vault.connect(user).withdrawAll(dai.address)) + await expect(vault.connect(user).withdrawAll()) .to.emit(vault, 'Withdraw') .withArgs(user.address, ether('999')); }); @@ -402,46 +286,4 @@ describe('Vault', () => { expect(await vault.available(dai.address)).to.equal(1000 * 0.95); }); }); - - describe('swap', () => { - beforeEach(async () => { - await manager.connect(treasury).setAllowedToken(dai.address, true); - await manager.connect(treasury).setAllowedToken(usdc.address, true); - await manager.connect(treasury).setAllowedVault(vault.address, true); - await manager.connect(treasury).setAllowedController(controller.address, true); - await manager.connect(treasury).setAllowedConverter(converter.address, true); - await controller.connect(deployer).setConverter(vault.address, converter.address); - await manager.connect(deployer).setController(vault.address, controller.address); - await expect(manager.connect(deployer).addToken(vault.address, dai.address)) - .to.emit(manager, 'TokenAdded') - .withArgs(vault.address, dai.address); - await expect(manager.connect(deployer).addToken(vault.address, usdc.address)) - .to.emit(manager, 'TokenAdded') - .withArgs(vault.address, usdc.address); - expect((await vault.getTokens()).length).to.equal(2); - await expect(vault.connect(user).deposit(dai.address, ether('1000'))) - .to.emit(vault, 'Deposit') - .withArgs(user.address, ether('1000')); - }); - - it('should revert when called by an address other than strategist', async () => { - await expect( - vault.connect(user).swap(dai.address, usdc.address, 1) - ).to.be.revertedWith('!strategist'); - }); - - it('should revert when halted', async () => { - await manager.setHalted(); - await expect( - vault.connect(user).swap(dai.address, usdc.address, 1) - ).to.be.revertedWith('halted'); - }); - - it('should swap tokens for the desired tokens', async () => { - expect(await dai.balanceOf(vault.address)).to.be.equal(ether('1000')); - await vault.connect(deployer).swap(dai.address, usdc.address, 1); - expect(await dai.balanceOf(vault.address)).to.be.equal(0); - expect(await usdc.balanceOf(vault.address)).to.be.above(999000000); - }); - }); }); diff --git a/test/v3/VaultHelper.test.js b/test/v3/VaultHelper.test.js index 0ad927de..c2d79063 100644 --- a/test/v3/VaultHelper.test.js +++ b/test/v3/VaultHelper.test.js @@ -8,22 +8,31 @@ const { parseEther } = ethers.utils; const ether = parseEther; describe('VaultHelper', () => { - let deployer, user; - let dai, usdc, usdt, vault, gauge, vaultHelper; + let deployer, treasury, user; + let dai, usdc, usdt, manager, vault, controller, gauge, gaugeController, vaultHelper; beforeEach(async () => { - await deployments.fixture(['v3', 'NativeStrategyCurve3Crv']); - [deployer, , , user] = await ethers.getSigners(); + await deployments.fixture('test'); + [deployer, treasury, , user] = await ethers.getSigners(); const DAI = await deployments.get('DAI'); dai = await ethers.getContractAt('MockERC20', DAI.address); const USDC = await deployments.get('USDC'); usdc = await ethers.getContractAt('MockERC20', USDC.address); const USDT = await deployments.get('USDT'); usdt = await ethers.getContractAt('MockERC20', USDT.address); + const Manager = await deployments.get('Manager'); + manager = await ethers.getContractAt('Manager', Manager.address); + const Controller = await deployments.get('Controller'); + controller = await ethers.getContractAt('Controller', Controller.address); const Vault = await deployments.get('VaultStables'); vault = await ethers.getContractAt('Vault', Vault.address); const Gauge = await deployments.get('VaultStablesGauge'); gauge = await ethers.getContractAt('LiquidityGaugeV2', Gauge.address); + const GaugeController = await deployments.get('GaugeController'); + gaugeController = await ethers.getContractAt( + 'GaugeController', + GaugeController.address + ); const VaultHelper = await deployments.get('VaultHelper'); vaultHelper = await ethers.getContractAt('VaultHelper', VaultHelper.address); @@ -34,6 +43,19 @@ describe('VaultHelper', () => { await usdt.connect(user).approve(VaultHelper.address, ethers.constants.MaxUint256); await gauge.connect(user).approve(VaultHelper.address, ethers.constants.MaxUint256); await vault.connect(user).approve(VaultHelper.address, ethers.constants.MaxUint256); + await manager.setAllowedVault(vault.address, true); + await manager.setGovernance(treasury.address); + await manager.connect(treasury).setAllowedController(controller.address, true); + await manager.setController(vault.address, controller.address); + await manager.connect(treasury).setAllowedToken(dai.address, true); + await manager.addToken(vault.address, dai.address); + await gaugeController['add_type(string,uint256)']('vault', ether('1')); + await gaugeController['add_gauge(address,int128,uint256)']( + gauge.address, + 0, + ether('1') + ); + await vault.connect(deployer).setGauge(gauge.address); }); describe('depositVault', () => { @@ -41,9 +63,7 @@ describe('VaultHelper', () => { it('should give gauge tokens to the user', async () => { expect(await vault.balanceOf(user.address)).to.be.equal(0); expect(await gauge.balanceOf(user.address)).to.be.equal(0); - await vaultHelper - .connect(user) - .depositVault(vault.address, dai.address, ether('100')); + await vaultHelper.connect(user).depositVault(vault.address, ether('100')); expect(await vault.balanceOf(user.address)).to.be.equal(0); expect(await gauge.balanceOf(user.address)).to.be.equal(ether('100')); }); @@ -57,66 +77,22 @@ describe('VaultHelper', () => { it('should give vault tokens to the user', async () => { expect(await vault.balanceOf(user.address)).to.be.equal(0); expect(await gauge.balanceOf(user.address)).to.be.equal(0); - await vaultHelper - .connect(user) - .depositVault(vault.address, dai.address, ether('100')); + await vaultHelper.connect(user).depositVault(vault.address, ether('100')); expect(await vault.balanceOf(user.address)).to.be.equal(ether('100')); expect(await gauge.balanceOf(user.address)).to.be.equal(0); }); }); }); - describe('depositMultipleVault', () => { - context('when the gauge is set', () => { - it('should give gauge tokens to the user', async () => { - expect(await vault.balanceOf(user.address)).to.be.equal(0); - expect(await gauge.balanceOf(user.address)).to.be.equal(0); - await vaultHelper - .connect(user) - .depositMultipleVault( - vault.address, - [dai.address, usdc.address], - [ether('100'), '100000000'] - ); - expect(await vault.balanceOf(user.address)).to.be.equal(0); - expect(await gauge.balanceOf(user.address)).to.be.equal(ether('200')); - }); - }); - - context('when the gauge is unset', () => { - beforeEach(async () => { - await vault.connect(deployer).setGauge(ethers.constants.AddressZero); - }); - - it('should give vault tokens to the user', async () => { - expect(await vault.balanceOf(user.address)).to.be.equal(0); - expect(await gauge.balanceOf(user.address)).to.be.equal(0); - await vaultHelper - .connect(user) - .depositMultipleVault( - vault.address, - [dai.address, usdc.address], - [ether('100'), '100000000'] - ); - expect(await vault.balanceOf(user.address)).to.be.equal(ether('200')); - expect(await gauge.balanceOf(user.address)).to.be.equal(0); - }); - }); - }); - describe('withdrawVault', () => { context('when the gauge is set', () => { beforeEach(async () => { - await vaultHelper - .connect(user) - .depositVault(vault.address, dai.address, ether('100')); + await vaultHelper.connect(user).depositVault(vault.address, ether('100')); expect(await gauge.balanceOf(user.address)).to.be.equal(ether('100')); }); it('should withdraw from the gauge and vault', async () => { - await vaultHelper - .connect(user) - .withdrawVault(vault.address, dai.address, ether('100')); + await vaultHelper.connect(user).withdrawVault(vault.address, ether('100')); expect(await gauge.balanceOf(user.address)).to.be.equal(0); expect(await vault.balanceOf(user.address)).to.be.equal(0); expect(await dai.balanceOf(user.address)).to.be.equal(ether('99999999.9')); @@ -126,16 +102,12 @@ describe('VaultHelper', () => { context('when the gauge is unset', () => { beforeEach(async () => { await vault.connect(deployer).setGauge(ethers.constants.AddressZero); - await vaultHelper - .connect(user) - .depositVault(vault.address, dai.address, ether('100')); + await vaultHelper.connect(user).depositVault(vault.address, ether('100')); expect(await vault.balanceOf(user.address)).to.be.equal(ether('100')); }); it('should withdraw from the vault', async () => { - await vaultHelper - .connect(user) - .withdrawVault(vault.address, dai.address, ether('100')); + await vaultHelper.connect(user).withdrawVault(vault.address, ether('100')); expect(await gauge.balanceOf(user.address)).to.be.equal(0); expect(await vault.balanceOf(user.address)).to.be.equal(0); expect(await dai.balanceOf(user.address)).to.be.equal(ether('99999999.9')); From 0c2298cdc27a6ae5766b23777c22aa7f5f4363c1 Mon Sep 17 00:00:00 2001 From: transferAndCall Date: Sun, 10 Oct 2021 13:47:52 -0500 Subject: [PATCH 2/2] Refactor to separate vault token from vault --- contracts/v3/Manager.sol | 56 ++----- contracts/v3/Vault.sol | 102 ++++++------- contracts/v3/VaultHelper.sol | 12 +- contracts/v3/VaultToken.sol | 41 +++++- contracts/v3/controllers/Controller.sol | 15 +- contracts/v3/controllers/LegacyController.sol | 2 +- contracts/v3/interfaces/IManager.sol | 6 +- contracts/v3/interfaces/IVault.sol | 3 +- contracts/v3/interfaces/IVaultToken.sol | 8 + deploy/v3/3.1.Vault3CRV.js | 26 ++-- deploy/v3/99.Test.js | 10 +- test/v3/Controller.test.js | 31 ++-- test/v3/Gauges.test.js | 21 +-- test/v3/Harvester.test.js | 14 +- test/v3/LegacyController.test.js | 13 +- test/v3/Manager.test.js | 138 ++++++------------ test/v3/Vault.test.js | 36 ++--- test/v3/VaultHelper.test.js | 34 +++-- 18 files changed, 250 insertions(+), 318 deletions(-) create mode 100644 contracts/v3/interfaces/IVaultToken.sol diff --git a/contracts/v3/Manager.sol b/contracts/v3/Manager.sol index 85132e1b..782f4ecb 100644 --- a/contracts/v3/Manager.sol +++ b/contracts/v3/Manager.sol @@ -56,15 +56,12 @@ contract Manager is IManager { mapping(address => bool) public override allowedControllers; mapping(address => bool) public override allowedConverters; mapping(address => bool) public override allowedStrategies; - mapping(address => bool) public override allowedTokens; mapping(address => bool) public override allowedVaults; // vault => controller mapping(address => address) public override controllers; // vault => token mapping(address => address) internal tokens; - // token => vault - mapping(address => address) public override vaults; event AllowedController( address indexed _controller, @@ -78,10 +75,6 @@ contract Manager is IManager { address indexed _strategy, bool _allowed ); - event AllowedToken( - address indexed _token, - bool _allowed - ); event AllowedVault( address indexed _vault, bool _allowed @@ -100,13 +93,12 @@ contract Manager is IManager { event SetStrategist( address indexed _strategist ); - event TokenAdded( + event VaultAdded( address indexed _vault, address indexed _token ); - event TokenRemoved( - address indexed _vault, - address indexed _token + event VaultRemoved( + address indexed _vault ); /** @@ -186,23 +178,6 @@ contract Manager is IManager { emit AllowedStrategy(_strategy, _allowed); } - /** - * @notice Sets the permission for the given token - * @param _token The address of the token - * @param _allowed The status of if it is allowed - */ - function setAllowedToken( - address _token, - bool _allowed - ) - external - notHalted - onlyGovernance - { - allowedTokens[_token] = _allowed; - emit AllowedToken(_token, _allowed); - } - /** * @notice Sets the permission for the given vault * @param _vault The address of the vault @@ -416,24 +391,20 @@ contract Manager is IManager { /** * @notice Adds a token to be able to be deposited for a given vault * @param _vault The address of the vault - * @param _token The address of the token */ - function addToken( - address _vault, - address _token + function addVault( + address _vault ) external override notHalted onlyStrategist { - require(allowedTokens[_token], "!allowedTokens"); require(allowedVaults[_vault], "!allowedVaults"); require(tokens[_vault] == address(0), "!_vault"); - require(vaults[_token] == address(0), "!_token"); - vaults[_token] = _vault; + address _token = IVault(_vault).getToken(); tokens[_vault] = _token; - emit TokenAdded(_vault, _token); + emit VaultAdded(_vault, _token); } /** @@ -458,22 +429,19 @@ contract Manager is IManager { /** * @notice Removes a token from being able to be deposited for a given vault * @param _vault The address of the vault - * @param _token The address of the token */ - function removeToken( - address _vault, - address _token + function removeVault( + address _vault ) external override notHalted onlyStrategist { - require(tokens[_vault] == _token, "!_token"); - require(vaults[_token] == _vault, "!_vault"); + require(tokens[_vault] != address(0), "!_vault"); delete tokens[_vault]; - delete vaults[_token]; - emit TokenRemoved(_vault, _token); + delete allowedVaults[_vault]; + emit VaultRemoved(_vault); } /** diff --git a/contracts/v3/Vault.sol b/contracts/v3/Vault.sol index ff15feef..45238deb 100644 --- a/contracts/v3/Vault.sol +++ b/contracts/v3/Vault.sol @@ -8,12 +8,11 @@ import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/GSN/Context.sol"; -import "./VaultToken.sol"; - import "./interfaces/IManager.sol"; import "./interfaces/IController.sol"; import "./interfaces/IConverter.sol"; import "./interfaces/IVault.sol"; +import "./interfaces/IVaultToken.sol"; import "./interfaces/ExtendedIERC20.sol"; /** @@ -21,7 +20,7 @@ import "./interfaces/ExtendedIERC20.sol"; * @notice The vault is where users deposit and withdraw * like-kind assets that have been added by governance. */ -contract Vault is VaultToken, IVault { +contract Vault is IVault { using Address for address; using SafeMath for uint256; using SafeERC20 for IERC20; @@ -29,6 +28,8 @@ contract Vault is VaultToken, IVault { uint256 public constant MAX = 10000; IManager public immutable override manager; + IERC20 public immutable token; + IVaultToken public immutable vaultToken; // Strategist-updated variables address public override gauge; @@ -40,19 +41,20 @@ contract Vault is VaultToken, IVault { event Earn(address indexed token, uint256 amount); /** - * @param _name The name of the vault token for depositors - * @param _symbol The symbol of the vault token for depositors + * @param _depositToken The address of the deposit token of the vault + * @param _vaultToken The address of the share token for the vault * @param _manager The address of the vault manager contract */ constructor( - string memory _name, - string memory _symbol, + address _depositToken, + address _vaultToken, address _manager ) public - VaultToken(_name, _symbol) { manager = IManager(_manager); + token = IERC20(_depositToken); + vaultToken = IVaultToken(_vaultToken); min = 9500; totalDepositCap = 10000000 ether; } @@ -124,13 +126,12 @@ contract Vault is VaultToken, IVault { onlyHarvester { require(manager.allowedStrategies(_strategy), "!_strategy"); - address _token = getToken(); IController _controller = IController(manager.controllers(address(this))); if (_controller.investEnabled()) { - uint256 _balance = available(_token); - IERC20(_token).safeTransfer(address(_controller), _balance); - _controller.earn(_strategy, _token, _balance); - emit Earn(_token, _balance); + uint256 _balance = available(); + token.safeTransfer(address(_controller), _balance); + _controller.earn(_strategy, address(token), _balance); + emit Earn(address(token), _balance); } } @@ -151,29 +152,26 @@ contract Vault is VaultToken, IVault { returns (uint256 _shares) { require(_amount > 0, "!_amount"); - address _token = getToken(); uint256 _balance = balance(); - uint256 _before = IERC20(_token).balanceOf(address(this)); - IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount); - _amount = IERC20(_token).balanceOf(address(this)).sub(_before); - - if (_amount > 0) { - _amount = _normalizeDecimals(_token, _amount); + uint256 _before = token.balanceOf(address(this)); + token.safeTransferFrom(msg.sender, address(this), _amount); + _amount = token.balanceOf(address(this)).sub(_before); + uint256 _supply = IERC20(address(vaultToken)).totalSupply(); - if (totalSupply() > 0) { - _amount = (_amount.mul(totalSupply())).div(_balance); - } + _amount = _normalizeDecimals(_amount); - _shares = _amount; + if (_supply > 0) { + _amount = (_amount.mul(_supply)).div(_balance); } - if (_shares > 0) { - _mint(msg.sender, _shares); - require(totalSupply() <= totalDepositCap, ">totalDepositCap"); - emit Deposit(msg.sender, _shares); - } + _shares = _amount; + + require(_shares > 0, "shares=0"); + require(_supply.add(_shares) <= totalDepositCap, ">totalDepositCap"); + vaultToken.mint(msg.sender, _shares); + emit Deposit(msg.sender, _shares); } /** @@ -186,9 +184,8 @@ contract Vault is VaultToken, IVault { public override { - address _output = getToken(); - uint256 _amount = (balance().mul(_shares)).div(totalSupply()); - _burn(msg.sender, _shares); + uint256 _amount = (balance().mul(_shares)).div(IERC20(address(vaultToken)).totalSupply()); + vaultToken.burn(msg.sender, _shares); uint256 _withdrawalProtectionFee = manager.withdrawalProtectionFee(); if (_withdrawalProtectionFee > 0) { @@ -196,21 +193,21 @@ contract Vault is VaultToken, IVault { _amount = _amount.sub(_withdrawalProtection); } - uint256 _balance = IERC20(_output).balanceOf(address(this)); + uint256 _balance = token.balanceOf(address(this)); if (_balance < _amount) { IController _controller = IController(manager.controllers(address(this))); uint256 _toWithdraw = _amount.sub(_balance); if (_controller.strategies() > 0) { - _controller.withdraw(_output, _toWithdraw); + _controller.withdraw(address(token), _toWithdraw); } - uint256 _after = IERC20(_output).balanceOf(address(this)); + uint256 _after = token.balanceOf(address(this)); uint256 _diff = _after.sub(_balance); if (_diff < _toWithdraw) { _amount = _after; } } - IERC20(_output).safeTransfer(msg.sender, _amount); + token.safeTransfer(msg.sender, _amount); emit Withdraw(msg.sender, _amount); } @@ -221,7 +218,7 @@ contract Vault is VaultToken, IVault { external override { - withdraw(balanceOf(msg.sender)); + withdraw(IERC20(address(vaultToken)).balanceOf(msg.sender)); } /** @@ -232,17 +229,14 @@ contract Vault is VaultToken, IVault { * @notice Returns the amount of tokens available to be sent to strategies * @dev Custom logic in here for how much the vault allows to be borrowed * @dev Sets minimum required on-hand to keep small withdrawals cheap - * @param _token The address of the token */ - function available( - address _token - ) + function available() public view override returns (uint256) { - return IERC20(_token).balanceOf(address(this)).mul(min).div(MAX); + return token.balanceOf(address(this)).mul(min).div(MAX); } /** @@ -265,8 +259,7 @@ contract Vault is VaultToken, IVault { view returns (uint256) { - address _token = getToken(); - return _normalizeDecimals(_token, IERC20(_token).balanceOf(address(this))); + return _normalizeDecimals(token.balanceOf(address(this))); } /** @@ -278,15 +271,16 @@ contract Vault is VaultToken, IVault { override returns (uint256) { - if (totalSupply() > 0) { - return balance().mul(1e18).div(totalSupply()); + uint256 _supply = IERC20(address(vaultToken)).totalSupply(); + if (_supply > 0) { + return balance().mul(1e18).div(_supply); } else { return balance(); } } /** - * @notice Returns an array of the tokens for this vault + * @notice Returns the deposit token for the vault */ function getToken() public @@ -294,7 +288,16 @@ contract Vault is VaultToken, IVault { override returns (address) { - return manager.getToken(address(this)); + return address(token); + } + + function getLPToken() + external + view + override + returns (address) + { + return address(vaultToken); } /** @@ -313,14 +316,13 @@ contract Vault is VaultToken, IVault { } function _normalizeDecimals( - address _token, uint256 _amount ) internal view returns (uint256) { - uint256 _decimals = uint256(ExtendedIERC20(_token).decimals()); + uint256 _decimals = uint256(ExtendedIERC20(address(token)).decimals()); if (_decimals < 18) { _amount = _amount.mul(10**(18-_decimals)); } diff --git a/contracts/v3/VaultHelper.sol b/contracts/v3/VaultHelper.sol index fd5484e9..ab758dd9 100644 --- a/contracts/v3/VaultHelper.sol +++ b/contracts/v3/VaultHelper.sol @@ -32,18 +32,19 @@ contract VaultHelper { { require(_amount > 0, "!_amount"); address _token = IVault(_vault).getToken(); + address _vaultToken = IVault(_vault).getLPToken(); IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount); IERC20(_token).safeApprove(_vault, 0); IERC20(_token).safeApprove(_vault, _amount); uint256 _shares = IVault(_vault).deposit(_amount); address _gauge = IVault(_vault).gauge(); if (_gauge != address(0)) { - IERC20(_vault).safeApprove(_gauge, 0); - IERC20(_vault).safeApprove(_gauge, _shares); + IERC20(_vaultToken).safeApprove(_gauge, 0); + IERC20(_vaultToken).safeApprove(_gauge, _shares); ILiquidityGaugeV2(_gauge).deposit(_shares); IERC20(_gauge).safeTransfer(msg.sender, _shares); } else { - IERC20(_vault).safeTransfer(msg.sender, _shares); + IERC20(_vaultToken).safeTransfer(msg.sender, _shares); } } @@ -55,13 +56,14 @@ contract VaultHelper { { address _gauge = IVault(_vault).gauge(); address _token = IVault(_vault).getToken(); + address _vaultToken = IVault(_vault).getLPToken(); if (_gauge != address(0)) { IERC20(_gauge).safeTransferFrom(msg.sender, address(this), _amount); ILiquidityGaugeV2(_gauge).withdraw(_amount); - IVault(_vault).withdraw(IERC20(_vault).balanceOf(address(this))); + IVault(_vault).withdraw(IERC20(_vaultToken).balanceOf(address(this))); IERC20(_token).safeTransfer(msg.sender, IERC20(_token).balanceOf(address(this))); } else { - IERC20(_vault).safeTransferFrom(msg.sender, address(this), _amount); + IERC20(_vaultToken).safeTransferFrom(msg.sender, address(this), _amount); IVault(_vault).withdraw(_amount); IERC20(_token).safeTransfer(msg.sender, IERC20(_token).balanceOf(address(this))); } diff --git a/contracts/v3/VaultToken.sol b/contracts/v3/VaultToken.sol index c2b34f0d..589c40bd 100644 --- a/contracts/v3/VaultToken.sol +++ b/contracts/v3/VaultToken.sol @@ -4,22 +4,52 @@ pragma solidity 0.6.12; import "../vendor/LinkToken/token/LinkERC20.sol"; import "../vendor/LinkToken/ERC677Token.sol"; +import "./interfaces/IManager.sol"; +import "./interfaces/IVaultToken.sol"; + /** * @notice Vault Token * @dev Contract has been copied from: * https://github.com/smartcontractkit/LinkToken/blob/master/contracts/v0.6/LinkToken.sol * with modification made to specify name and symbol, deploys with 0 total supply */ -contract VaultToken is LinkERC20, ERC677Token { +contract VaultToken is IVaultToken, LinkERC20, ERC677Token { + + IManager public immutable manager; constructor( string memory _name, - string memory _symbol + string memory _symbol, + address _manager ) public ERC20(_name, _symbol) // solhint-disable-next-line no-empty-blocks - {} + { + manager = IManager(_manager); + } + + function mint( + address _account, + uint256 _amount + ) + external + override + onlyVault + { + _mint(_account, _amount); + } + + function burn( + address _account, + uint256 _amount + ) + external + override + onlyVault + { + _burn(_account, _amount); + } /** * @dev Moves tokens `amount` from `sender` to `recipient`. @@ -73,4 +103,9 @@ contract VaultToken is LinkERC20, ERC677Token { require(_recipient != address(this), "!validAddress"); _; } + + modifier onlyVault() { + require(manager.allowedVaults(msg.sender), "!vault"); + _; + } } diff --git a/contracts/v3/controllers/Controller.sol b/contracts/v3/controllers/Controller.sol index 7e39b394..a72f2aa7 100644 --- a/contracts/v3/controllers/Controller.sol +++ b/contracts/v3/controllers/Controller.sol @@ -411,7 +411,7 @@ contract Controller is IController { override notHalted onlyStrategy(_strategy) - onlyVault(_token) + onlyVault() { // get the want token of the strategy address _want = IStrategy(_strategy).want(); @@ -444,12 +444,12 @@ contract Controller is IController { ) external override - onlyVault(_token) + onlyVault() { ( address[] memory _strategies, uint256[] memory _amounts - ) = getBestStrategyWithdraw(_token, _amount); + ) = getBestStrategyWithdraw(msg.sender, _amount); for (uint i = 0; i < _strategies.length; i++) { // getBestStrategyWithdraw will return arrays larger than needed // if this happens, simply exit the loop @@ -573,11 +573,11 @@ contract Controller is IController { * from this function will always be the same length as the amount of strategies for * a token. Check that _strategies[i] != address(0) when consuming to know when to * break out of the loop. - * @param _token The address of the token + * @param _vault The address of the vault * @param _amount The amount that will be withdrawn */ function getBestStrategyWithdraw( - address _token, + address _vault, uint256 _amount ) internal @@ -588,7 +588,6 @@ contract Controller is IController { ) { // get the length of strategies for a single token - address _vault = manager.vaults(_token); uint256 k = _vaultDetails[_vault].strategies.length; // initialize fixed-length memory arrays _strategies = new address[](k); @@ -675,8 +674,8 @@ contract Controller is IController { /** * @notice Reverts if the caller is not the vault for the given token */ - modifier onlyVault(address _token) { - require(msg.sender == manager.vaults(_token), "!vault"); + modifier onlyVault() { + require(manager.allowedVaults(msg.sender), "!vault"); _; } } diff --git a/contracts/v3/controllers/LegacyController.sol b/contracts/v3/controllers/LegacyController.sol index 74e9b3c3..53849b8e 100644 --- a/contracts/v3/controllers/LegacyController.sol +++ b/contracts/v3/controllers/LegacyController.sol @@ -118,7 +118,7 @@ contract LegacyController is ILegacyController { returns (uint256) { return token.balanceOf(address(this)) - .add(IERC20(address(vault)).balanceOf(address(this))); + .add(IERC20(vault.getLPToken()).balanceOf(address(this))); } /** diff --git a/contracts/v3/interfaces/IManager.sol b/contracts/v3/interfaces/IManager.sol index 9264a3d7..72ba5ff0 100644 --- a/contracts/v3/interfaces/IManager.sol +++ b/contracts/v3/interfaces/IManager.sol @@ -3,11 +3,10 @@ pragma solidity 0.6.12; interface IManager { - function addToken(address, address) external; + function addVault(address) external; function allowedControllers(address) external view returns (bool); function allowedConverters(address) external view returns (bool); function allowedStrategies(address) external view returns (bool); - function allowedTokens(address) external view returns (bool); function allowedVaults(address) external view returns (bool); function controllers(address) external view returns (address); function getHarvestFeeInfo() external view returns (address, address, uint256); @@ -19,13 +18,12 @@ interface IManager { function insurancePool() external view returns (address); function insurancePoolFee() external view returns (uint256); function pendingStrategist() external view returns (address); - function removeToken(address, address) external; + function removeVault(address) external; function stakingPool() external view returns (address); function stakingPoolShareFee() external view returns (uint256); function strategist() external view returns (address); function treasury() external view returns (address); function treasuryFee() external view returns (uint256); - function vaults(address) external view returns (address); function withdrawalProtectionFee() external view returns (uint256); function yaxis() external view returns (address); } diff --git a/contracts/v3/interfaces/IVault.sol b/contracts/v3/interfaces/IVault.sol index f0de146e..437e0e91 100644 --- a/contracts/v3/interfaces/IVault.sol +++ b/contracts/v3/interfaces/IVault.sol @@ -5,11 +5,12 @@ pragma solidity 0.6.12; import "./IManager.sol"; interface IVault { - function available(address _token) external view returns (uint256); + function available() external view returns (uint256); function balance() external view returns (uint256); function deposit(uint256 _amount) external returns (uint256); function earn(address _strategy) external; function gauge() external returns (address); + function getLPToken() external view returns (address); function getPricePerFullShare() external view returns (uint256); function getToken() external view returns (address); function manager() external view returns (IManager); diff --git a/contracts/v3/interfaces/IVaultToken.sol b/contracts/v3/interfaces/IVaultToken.sol new file mode 100644 index 00000000..a79a6158 --- /dev/null +++ b/contracts/v3/interfaces/IVaultToken.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.2; + +interface IVaultToken { + function mint(address,uint256) external; + function burn(address,uint256) external; +} diff --git a/deploy/v3/3.1.Vault3CRV.js b/deploy/v3/3.1.Vault3CRV.js index 6cb208cb..a2885f1e 100644 --- a/deploy/v3/3.1.Vault3CRV.js +++ b/deploy/v3/3.1.Vault3CRV.js @@ -12,18 +12,25 @@ module.exports = async ({ getChainId, getNamedAccounts, deployments }) => { T3CRV = t3crv.address; } + const VaultToken = await deploy('VaultToken3CRV', { + contract: 'VaultToken', + from: deployer, + log: true, + args: ['yAxis 3CRV Vault', 'V:3CRV', Manager.address] + }); + const Vault = await deploy('Vault3CRV', { contract: 'Vault', from: deployer, log: true, - args: ['yAxis 3CRV Canonical Vault', 'CV:3CRV', Manager.address] + args: [T3CRV, VaultToken.address, Manager.address] }); const Gauge = await deploy('Vault3CRVGauge', { contract: 'LiquidityGaugeV2', from: deployer, log: true, - args: [Vault.address, Minter.address, GaugeProxy.address] + args: [VaultToken.address, Minter.address, GaugeProxy.address] }); if (Gauge.newlyDeployed) { @@ -42,20 +49,7 @@ module.exports = async ({ getChainId, getNamedAccounts, deployments }) => { Vault.address, true ); - await execute( - 'Manager', - { from: deployer, log: true }, - 'setAllowedToken', - T3CRV, - true - ); - await execute( - 'Manager', - { from: deployer, log: true }, - 'addToken', - Vault.address, - T3CRV - ); + await execute('Manager', { from: deployer, log: true }, 'addVault', Vault.address); await execute( 'Manager', { from: deployer, log: true }, diff --git a/deploy/v3/99.Test.js b/deploy/v3/99.Test.js index 7584a00c..7c5945e7 100644 --- a/deploy/v3/99.Test.js +++ b/deploy/v3/99.Test.js @@ -218,10 +218,14 @@ module.exports = async ({ getNamedAccounts, deployments }) => { from: deployer, args: ['0x0000000000000000000000000000000000000000'] }); - const Vault = await deployments.deploy('VaultStables', { + const VaultToken = await deployments.deploy('VaultToken', { + from: deployer, + args: ['VaultStables', 'CV:S', Manager.address] + }); + await deployments.deploy('VaultStables', { contract: 'Vault', from: deployer, - args: ['Vault: Stables', 'MV:S', Manager.address] + args: [DAI.address, VaultToken.address, Manager.address] }); const MinterWrapper = await deployments.deploy('MinterWrapper', { from: deployer, @@ -252,7 +256,7 @@ module.exports = async ({ getNamedAccounts, deployments }) => { contract: 'LiquidityGaugeV2', from: deployer, log: true, - args: [Vault.address, Minter.address, GaugeProxy.address] + args: [VaultToken.address, Minter.address, GaugeProxy.address] }); await deployments.deploy('VaultHelper', { from: deployer, diff --git a/test/v3/Controller.test.js b/test/v3/Controller.test.js index b742d749..27667386 100644 --- a/test/v3/Controller.test.js +++ b/test/v3/Controller.test.js @@ -79,8 +79,7 @@ describe('Controller', () => { context('when the strategy is allowed', () => { beforeEach(async () => { await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); }); it('should revert if called by an address other than the strategist', async () => { @@ -189,8 +188,7 @@ describe('Controller', () => { await controller.connect(deployer).setConverter(vault.address, converter.address); await manager.connect(deployer).setHarvester(harvester.address); await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); }); it('should revert if called by an address other than the strategist', async () => { @@ -298,8 +296,7 @@ describe('Controller', () => { context('when the strategy is allowed', () => { beforeEach(async () => { await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); }); @@ -337,8 +334,7 @@ describe('Controller', () => { await manager.connect(deployer).setAllowedController(controller.address, true); await manager.connect(deployer).setController(vault.address, controller.address); await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); }); it('should revert if called by an address other than the strategist', async () => { @@ -521,8 +517,7 @@ describe('Controller', () => { .setConverter(vault.address, converter.address); await manager.connect(deployer).setHarvester(harvester.address); await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await t3crv.connect(user).faucet(1000); await t3crv.connect(user).transfer(strategyCrv.address, 1000); @@ -571,8 +566,7 @@ describe('Controller', () => { await manager.connect(deployer).setHarvester(harvester.address); await harvester.connect(deployer).setHarvester(deployer.address, true); await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await manager.setController(vault.address, controller.address); await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); @@ -623,8 +617,7 @@ describe('Controller', () => { .connect(deployer) .setConverter(vault.address, converter.address); await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await manager.setController(vault.address, controller.address); await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000000); @@ -674,9 +667,7 @@ describe('Controller', () => { await manager.connect(deployer).setHarvester(harvester.address); await harvester.connect(deployer).setHarvester(deployer.address, true); await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.connect(deployer).setAllowedToken(t3crv.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await manager.setController(vault.address, controller.address); await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); @@ -716,8 +707,7 @@ describe('Controller', () => { await manager.connect(deployer).setHarvester(harvester.address); await harvester.connect(deployer).setHarvester(deployer.address, true); await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await manager.setController(vault.address, controller.address); await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); await dai.connect(user).faucet(1000); @@ -751,8 +741,7 @@ describe('Controller', () => { await manager.connect(deployer).setHarvester(harvester.address); await harvester.connect(deployer).setHarvester(deployer.address, true); await manager.connect(deployer).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await controller.addStrategy(vault.address, strategyCrv.address, 0, 86400); }); diff --git a/test/v3/Gauges.test.js b/test/v3/Gauges.test.js index b893ec58..2e973cef 100644 --- a/test/v3/Gauges.test.js +++ b/test/v3/Gauges.test.js @@ -11,25 +11,20 @@ const ether = parseEther; describe('Gauges', () => { const MAXTIME = 1 * 365 * 86400; let deployer, treasury, user; - let controller, - t3crv, + let t3crv, gaugeController, gaugeProxy, - manager, minter, minterWrapper, vault3Crv, vault3CrvGauge, + vault3CrvToken, votingEscrow, yaxis; before(async () => { await deployments.fixture('v3'); [deployer, treasury, user] = await ethers.getSigners(); - const Manager = await deployments.get('Manager'); - manager = await ethers.getContractAt('Manager', Manager.address); - const Controller = await deployments.get('Controller'); - controller = await ethers.getContractAt('Controller', Controller.address); const YAXIS = await deployments.get('YaxisToken'); yaxis = await ethers.getContractAt('YaxisToken', YAXIS.address); const T3CRV = await deployments.get('T3CRV'); @@ -48,6 +43,8 @@ describe('Gauges', () => { minterWrapper = await ethers.getContractAt('MinterWrapper', MinterWrapper.address); const Minter = await deployments.get('Minter'); minter = await ethers.getContractAt('Minter', Minter.address); + const Vault3CRVToken = await deployments.get('VaultToken3CRV'); + vault3CrvToken = await ethers.getContractAt('VaultToken', Vault3CRVToken.address); const Vault3CRV = await deployments.get('Vault3CRV'); vault3Crv = await ethers.getContractAt('Vault', Vault3CRV.address); const Vault3CRVGauge = await deployments.get('Vault3CRVGauge'); @@ -68,7 +65,7 @@ describe('Gauges', () => { expect(await minter.controller()).to.be.equal(gaugeController.address); expect(await minterWrapper.token()).to.be.equal(yaxis.address); expect(await vault3CrvGauge.crv_token()).to.be.equal(minterWrapper.address); - expect(await vault3CrvGauge.lp_token()).to.be.equal(vault3Crv.address); + expect(await vault3CrvGauge.lp_token()).to.be.equal(vault3CrvToken.address); expect(await vault3CrvGauge.controller()).to.be.equal(gaugeController.address); expect(await vault3CrvGauge.admin()).to.be.equal(gaugeProxy.address); expect(await vault3CrvGauge.minter()).to.be.equal(minter.address); @@ -104,15 +101,11 @@ describe('Gauges', () => { it('should allow users to stake vault tokens in a gauge', async () => { await increaseTime(86400 * 7); - await manager.connect(deployer).setAllowedController(controller.address, true); - await manager.connect(deployer).setAllowedVault(vault3Crv.address, true); - await manager.connect(deployer).setAllowedToken(t3crv.address, true); - await manager.connect(deployer).setController(vault3Crv.address, controller.address); await t3crv.connect(user).faucet(ether('1000')); await t3crv.connect(user).approve(vault3Crv.address, ethers.constants.MaxUint256); await vault3Crv.connect(user).deposit(ether('1000')); - expect(await vault3Crv.balanceOf(user.address)).to.be.equal(ether('1000')); - await vault3Crv + expect(await vault3CrvToken.balanceOf(user.address)).to.be.equal(ether('1000')); + await vault3CrvToken .connect(user) .approve(vault3CrvGauge.address, ethers.constants.MaxUint256); expect(await vault3CrvGauge.balanceOf(user.address)).to.be.equal(0); diff --git a/test/v3/Harvester.test.js b/test/v3/Harvester.test.js index 21f4111e..d84e5244 100644 --- a/test/v3/Harvester.test.js +++ b/test/v3/Harvester.test.js @@ -48,22 +48,15 @@ describe('Harvester', () => { 'LegacyController', LegacyController.address ); - const Harvester = await deployments.deploy('Harvester', { - from: deployer.address, - args: [manager.address, controller.address, legacyController.address] - }); + const Harvester = await deployments.get('Harvester'); harvester = await ethers.getContractAt('Harvester', Harvester.address); - const StrategyCrv = await deployments.get('NativeStrategyCurve3Crv'); strategyCrv = await ethers.getContractAt( 'NativeStrategyCurve3Crv', StrategyCrv.address, deployer ); - const Vault = await deployments.deploy('Vault', { - from: deployer.address, - args: ['Vault: Stables', 'MV:S', manager.address] - }); + const Vault = await deployments.get('VaultStables'); vault = await ethers.getContractAt('Vault', Vault.address); await manager.setAllowedVault(vault.address, true); @@ -75,8 +68,7 @@ describe('Harvester', () => { await controller.connect(deployer).setConverter(vault.address, converter.address); await manager.connect(treasury).setHarvester(harvester.address); await manager.connect(treasury).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(treasury).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await harvester.setHarvester(deployer.address, true); }); diff --git a/test/v3/LegacyController.test.js b/test/v3/LegacyController.test.js index fe0dc4c5..709e9d12 100644 --- a/test/v3/LegacyController.test.js +++ b/test/v3/LegacyController.test.js @@ -9,8 +9,7 @@ const ether = parseEther; describe('LegacyController', () => { let deployer, user; - let dai, - t3crv, + let t3crv, vault, manager, metavault, @@ -25,8 +24,6 @@ describe('LegacyController', () => { [deployer, , , user] = await ethers.getSigners(); const T3CRV = await deployments.get('T3CRV'); t3crv = await ethers.getContractAt('MockERC20', T3CRV.address); - const DAI = await deployments.get('DAI'); - dai = await ethers.getContractAt('MockERC20', DAI.address); const Manager = await deployments.get('Manager'); manager = await ethers.getContractAt('Manager', Manager.address); const Harvester = await deployments.get('Harvester'); @@ -50,16 +47,12 @@ describe('LegacyController', () => { const Strategy = await deployments.get('NativeStrategyCurve3Crv'); strategy = await ethers.getContractAt('NativeStrategyCurve3Crv', Strategy.address); - const Vault = await deployments.deploy('Vault', { - from: deployer.address, - args: ['Vault: Stables', 'MV:S', manager.address] - }); + const Vault = await deployments.get('VaultStables'); vault = await ethers.getContractAt('Vault', Vault.address); await manager.connect(deployer).setAllowedVault(vault.address, true); await manager.connect(deployer).setAllowedStrategy(Strategy.address, true); - await manager.connect(deployer).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await manager.connect(deployer).setAllowedConverter(converter.address, true); await manager.connect(deployer).setAllowedController(legacyController.address, true); await manager.connect(deployer).setAllowedController(controller.address, true); diff --git a/test/v3/Manager.test.js b/test/v3/Manager.test.js index d7dbbe22..e9195f18 100644 --- a/test/v3/Manager.test.js +++ b/test/v3/Manager.test.js @@ -268,51 +268,14 @@ describe('Manager', () => { }); }); - describe('setAllowedToken', () => { - let token; - - beforeEach(async () => { - const Token = await ethers.getContractFactory('MockERC20'); - token = await Token.deploy('Token', 'TKN', 18); - await manager.connect(deployer).setGovernance(treasury.address); - }); - - it('should revert when called by non-governance address', async () => { - await expect( - manager.connect(deployer).setAllowedToken(token.address, true) - ).to.be.revertedWith('!governance'); - }); - - it('should revert if the manager is halted', async () => { - await manager.connect(deployer).setHalted(); - await expect( - manager.connect(deployer).setAllowedToken(token.address, true) - ).to.be.revertedWith('halted'); - }); - - it('should set the allowed token when called by governance', async () => { - await expect(manager.connect(treasury).setAllowedToken(token.address, true)) - .to.emit(manager, 'AllowedToken') - .withArgs(token.address, true); - expect(await manager.allowedTokens(token.address)).to.be.equal(true); - }); - - it('should unset the allowed token when called by governance', async () => { - await manager.connect(treasury).setAllowedToken(token.address, true); - expect(await manager.allowedTokens(token.address)).to.be.equal(true); - await expect(manager.connect(treasury).setAllowedToken(token.address, false)) - .to.emit(manager, 'AllowedToken') - .withArgs(token.address, false); - expect(await manager.allowedTokens(token.address)).to.be.equal(false); - }); - }); - describe('setAllowedVault', () => { - let vault; + let vault, vaultToken; beforeEach(async () => { + const VaultToken = await ethers.getContractFactory('VaultToken'); const Vault = await ethers.getContractFactory('Vault'); - vault = await Vault.deploy('Vault', 'V', manager.address); + vaultToken = await VaultToken.deploy('Vault', 'V', manager.address); + vault = await Vault.deploy(t3crv.address, vaultToken.address, manager.address); await manager.connect(deployer).setGovernance(treasury.address); }); @@ -325,8 +288,14 @@ describe('Manager', () => { it('should revert if the vault manager is not this manager', async () => { const FakeManager = await ethers.getContractFactory('Manager'); const fakeManager = await FakeManager.deploy(yaxis.address); + const NewVaultToken = await ethers.getContractFactory('VaultToken'); + vaultToken = await NewVaultToken.deploy('Bad Vault', 'BV', fakeManager.address); const NewVault = await ethers.getContractFactory('Vault'); - const newVault = await NewVault.deploy('Bad Vault', 'BV', fakeManager.address); + const newVault = await NewVault.deploy( + t3crv.address, + vaultToken.address, + fakeManager.address + ); await expect( manager.connect(treasury).setAllowedVault(newVault.address, true) ).to.be.revertedWith('!manager'); @@ -689,62 +658,37 @@ describe('Manager', () => { }); }); - describe('addToken', () => { - let vault; + describe('addVault', () => { + let vault, vaultToken; beforeEach(async () => { + const VaultToken = await ethers.getContractFactory('VaultToken'); const Vault = await ethers.getContractFactory('Vault'); - vault = await Vault.deploy('Vault', 'VLT', manager.address); + vaultToken = await VaultToken.deploy('Vault', 'V', manager.address); + vault = await Vault.deploy(t3crv.address, vaultToken.address, manager.address); await manager.connect(deployer).setGovernance(treasury.address); await manager.connect(treasury).setAllowedVault(vault.address, true); - await manager.connect(treasury).setAllowedToken(dai.address, true); }); it('should revert when called by non-strategist address', async () => { await expect( - manager - .connect(user) - .addToken(ethers.constants.AddressZero, ethers.constants.AddressZero) + manager.connect(user).addVault(ethers.constants.AddressZero) ).to.be.revertedWith('!strategist'); }); it('should revert if the manager is halted', async () => { await manager.connect(deployer).setHalted(); await expect( - manager - .connect(deployer) - .addToken(ethers.constants.AddressZero, ethers.constants.AddressZero) + manager.connect(deployer).addVault(ethers.constants.AddressZero) ).to.be.revertedWith('halted'); }); - it('should revert if token is not allowed', async () => { - await expect( - manager - .connect(deployer) - .addToken(ethers.constants.AddressZero, ethers.constants.AddressZero) - ).to.be.revertedWith('!allowedTokens'); - }); - it('should revert if vault is not allowed', async () => { + await manager.connect(treasury).setAllowedVault(vault.address, false); await expect( - manager.connect(deployer).addToken(ethers.constants.AddressZero, dai.address) + manager.connect(deployer).addVault(ethers.constants.AddressZero) ).to.be.revertedWith('!allowedVaults'); }); - - it('should add tokens within the maximum number', async () => { - await expect(manager.connect(deployer).addToken(vault.address, dai.address)) - .to.emit(manager, 'TokenAdded') - .withArgs(vault.address, dai.address); - }); - - it('should revert if the token is already added', async () => { - await expect(manager.connect(deployer).addToken(vault.address, dai.address)) - .to.emit(manager, 'TokenAdded') - .withArgs(vault.address, dai.address); - await expect( - manager.connect(deployer).addToken(vault.address, dai.address) - ).to.be.revertedWith('!_vault'); - }); }); describe('recoverToken', () => { @@ -775,54 +719,56 @@ describe('Manager', () => { }); }); - describe('removeToken', () => { - let vault; + describe('removeVault', () => { + let vault, vaultToken; beforeEach(async () => { + const VaultToken = await ethers.getContractFactory('VaultToken'); const Vault = await ethers.getContractFactory('Vault'); - vault = await Vault.deploy('Vault', 'VLT', manager.address); + vaultToken = await VaultToken.deploy('Vault', 'V', manager.address); + vault = await Vault.deploy(t3crv.address, vaultToken.address, manager.address); await manager.connect(deployer).setGovernance(treasury.address); await manager.connect(treasury).setAllowedVault(vault.address, true); - await manager.connect(treasury).setAllowedToken(dai.address, true); - await manager.connect(deployer).addToken(vault.address, dai.address); + await manager.connect(deployer).addVault(vault.address); }); it('should revert when called by non-strategist address', async () => { await expect( - manager - .connect(user) - .removeToken(ethers.constants.AddressZero, ethers.constants.AddressZero) + manager.connect(user).removeVault(ethers.constants.AddressZero) ).to.be.revertedWith('!strategist'); }); it('should revert if the manager is halted', async () => { await manager.connect(deployer).setHalted(); await expect( - manager - .connect(deployer) - .removeToken(ethers.constants.AddressZero, ethers.constants.AddressZero) + manager.connect(deployer).removeVault(ethers.constants.AddressZero) ).to.be.revertedWith('halted'); }); - it('should remove token', async () => { - await expect(manager.connect(deployer).removeToken(vault.address, dai.address)) - .to.emit(manager, 'TokenRemoved') - .withArgs(vault.address, dai.address); + it('should remove vault', async () => { + await expect(manager.connect(deployer).removeVault(vault.address)) + .to.emit(manager, 'VaultRemoved') + .withArgs(vault.address); }); - it('should revert if the token is not added', async () => { + it('should revert if the vault is not added', async () => { + await expect(manager.connect(deployer).removeVault(vault.address)) + .to.emit(manager, 'VaultRemoved') + .withArgs(vault.address); await expect( - manager.connect(deployer).removeToken(vault.address, usdc.address) - ).to.be.revertedWith('!_token'); + manager.connect(deployer).removeVault(vault.address) + ).to.be.revertedWith('!_vault'); }); }); describe('setController', () => { - let vault, newController; + let vault, vaultToken, newController; beforeEach(async () => { + const VaultToken = await ethers.getContractFactory('VaultToken'); const Vault = await ethers.getContractFactory('Vault'); - vault = await Vault.deploy('Vault', 'VLT', manager.address); + vaultToken = await VaultToken.deploy('Vault', 'V', manager.address); + vault = await Vault.deploy(t3crv.address, vaultToken.address, manager.address); await manager.connect(deployer).setGovernance(treasury.address); await manager.connect(treasury).setAllowedVault(vault.address, true); const NewController = await ethers.getContractFactory('Controller'); diff --git a/test/v3/Vault.test.js b/test/v3/Vault.test.js index fc8a4208..5cb007aa 100644 --- a/test/v3/Vault.test.js +++ b/test/v3/Vault.test.js @@ -9,7 +9,7 @@ const ether = parseEther; describe('Vault', () => { let deployer, treasury, user; - let dai, vault, manager, controller, harvester, strategyCrv, converter; + let dai, vault, vaultToken, manager, controller, harvester, strategyCrv, converter; beforeEach(async () => { await deployments.fixture('test'); @@ -29,6 +29,8 @@ describe('Vault', () => { deployer ); converter = await deployments.get('StablesConverter'); + const VaultToken = await deployments.get('VaultToken'); + vaultToken = await ethers.getContractAt('VaultToken', VaultToken.address); const Vault = await deployments.get('VaultStables'); vault = await ethers.getContractAt('Vault', Vault.address); @@ -39,8 +41,6 @@ describe('Vault', () => { }); it('should deploy with expected state', async () => { - expect(await vault.name()).to.equal('Vault: Stables'); - expect(await vault.symbol()).to.equal('MV:S'); expect(await vault.manager()).to.equal(manager.address); expect(await vault.min()).to.equal(9500); expect(await vault.totalDepositCap()).to.equal(ether('10000000')); @@ -100,9 +100,8 @@ describe('Vault', () => { await manager.connect(treasury).setAllowedConverter(converter.address, true); await controller.connect(deployer).setConverter(vault.address, converter.address); await manager.connect(treasury).setAllowedStrategy(strategyCrv.address, true); - await manager.connect(treasury).setAllowedToken(dai.address, true); - await expect(manager.addToken(vault.address, dai.address)) - .to.emit(manager, 'TokenAdded') + await expect(manager.addVault(vault.address)) + .to.emit(manager, 'VaultAdded') .withArgs(vault.address, dai.address); await manager.setController(vault.address, controller.address); await manager.connect(treasury).setHarvester(harvester.address); @@ -153,7 +152,7 @@ describe('Vault', () => { it('should revert when the vault is not set up', async () => { const NewVault = await deployments.deploy('Vault', { from: deployer.address, - args: ['Vault: Stables', 'MV:S2', manager.address] + args: [vaultToken.address, dai.address, manager.address] }); const newVault = await ethers.getContractAt('Vault', NewVault.address); await dai.connect(user).approve(NewVault.address, ethers.utils.parseEther('1000')); @@ -162,12 +161,11 @@ describe('Vault', () => { context('when the vault is set up', () => { beforeEach(async () => { - await manager.connect(treasury).setAllowedToken(dai.address, true); await manager.connect(treasury).setAllowedVault(vault.address, true); await manager.connect(treasury).setAllowedController(controller.address, true); await manager.setController(vault.address, controller.address); - await expect(manager.addToken(vault.address, dai.address)) - .to.emit(manager, 'TokenAdded') + await expect(manager.addVault(vault.address)) + .to.emit(manager, 'VaultAdded') .withArgs(vault.address, dai.address); }); @@ -187,24 +185,23 @@ describe('Vault', () => { }); it('should deposit single token', async () => { - expect(await vault.balanceOf(user.address)).to.equal(0); + expect(await vaultToken.balanceOf(user.address)).to.equal(0); await expect(vault.connect(user).deposit(ether('1000'))) .to.emit(vault, 'Deposit') .withArgs(user.address, ether('1000')); - expect(await vault.balanceOf(user.address)).to.equal(ether('1000')); - expect(await vault.totalSupply()).to.equal(ether('1000')); + expect(await vaultToken.balanceOf(user.address)).to.equal(ether('1000')); + expect(await vaultToken.totalSupply()).to.equal(ether('1000')); }); }); }); describe('withdraw', () => { beforeEach(async () => { - await manager.connect(treasury).setAllowedToken(dai.address, true); await manager.connect(treasury).setAllowedVault(vault.address, true); await manager.connect(treasury).setAllowedController(controller.address, true); await manager.connect(deployer).setController(vault.address, controller.address); - await expect(manager.connect(deployer).addToken(vault.address, dai.address)) - .to.emit(manager, 'TokenAdded') + await expect(manager.connect(deployer).addVault(vault.address)) + .to.emit(manager, 'VaultAdded') .withArgs(vault.address, dai.address); }); @@ -249,12 +246,11 @@ describe('Vault', () => { describe('withdrawAll', () => { beforeEach(async () => { - await manager.connect(treasury).setAllowedToken(dai.address, true); await manager.connect(treasury).setAllowedVault(vault.address, true); await manager.connect(treasury).setAllowedController(controller.address, true); await manager.connect(deployer).setController(vault.address, controller.address); - await expect(manager.connect(deployer).addToken(vault.address, dai.address)) - .to.emit(manager, 'TokenAdded') + await expect(manager.connect(deployer).addVault(vault.address)) + .to.emit(manager, 'VaultAdded') .withArgs(vault.address, dai.address); }); @@ -283,7 +279,7 @@ describe('Vault', () => { it('should get balance with minimum amount on-hand', async () => { await dai.connect(user).faucet(1000); await dai.connect(user).transfer(vault.address, 1000); - expect(await vault.available(dai.address)).to.equal(1000 * 0.95); + expect(await vault.available()).to.equal(1000 * 0.95); }); }); }); diff --git a/test/v3/VaultHelper.test.js b/test/v3/VaultHelper.test.js index c2d79063..742c4254 100644 --- a/test/v3/VaultHelper.test.js +++ b/test/v3/VaultHelper.test.js @@ -9,7 +9,16 @@ const ether = parseEther; describe('VaultHelper', () => { let deployer, treasury, user; - let dai, usdc, usdt, manager, vault, controller, gauge, gaugeController, vaultHelper; + let dai, + usdc, + usdt, + manager, + vault, + vaultToken, + controller, + gauge, + gaugeController, + vaultHelper; beforeEach(async () => { await deployments.fixture('test'); @@ -24,6 +33,8 @@ describe('VaultHelper', () => { manager = await ethers.getContractAt('Manager', Manager.address); const Controller = await deployments.get('Controller'); controller = await ethers.getContractAt('Controller', Controller.address); + const VaultToken = await deployments.get('VaultToken'); + vaultToken = await ethers.getContractAt('VaultToken', VaultToken.address); const Vault = await deployments.get('VaultStables'); vault = await ethers.getContractAt('Vault', Vault.address); const Gauge = await deployments.get('VaultStablesGauge'); @@ -42,13 +53,14 @@ describe('VaultHelper', () => { await usdc.connect(user).approve(VaultHelper.address, ethers.constants.MaxUint256); await usdt.connect(user).approve(VaultHelper.address, ethers.constants.MaxUint256); await gauge.connect(user).approve(VaultHelper.address, ethers.constants.MaxUint256); - await vault.connect(user).approve(VaultHelper.address, ethers.constants.MaxUint256); + await vaultToken + .connect(user) + .approve(VaultHelper.address, ethers.constants.MaxUint256); await manager.setAllowedVault(vault.address, true); await manager.setGovernance(treasury.address); await manager.connect(treasury).setAllowedController(controller.address, true); await manager.setController(vault.address, controller.address); - await manager.connect(treasury).setAllowedToken(dai.address, true); - await manager.addToken(vault.address, dai.address); + await manager.addVault(vault.address); await gaugeController['add_type(string,uint256)']('vault', ether('1')); await gaugeController['add_gauge(address,int128,uint256)']( gauge.address, @@ -61,10 +73,10 @@ describe('VaultHelper', () => { describe('depositVault', () => { context('when the gauge is set', () => { it('should give gauge tokens to the user', async () => { - expect(await vault.balanceOf(user.address)).to.be.equal(0); + expect(await vaultToken.balanceOf(user.address)).to.be.equal(0); expect(await gauge.balanceOf(user.address)).to.be.equal(0); await vaultHelper.connect(user).depositVault(vault.address, ether('100')); - expect(await vault.balanceOf(user.address)).to.be.equal(0); + expect(await vaultToken.balanceOf(user.address)).to.be.equal(0); expect(await gauge.balanceOf(user.address)).to.be.equal(ether('100')); }); }); @@ -75,10 +87,10 @@ describe('VaultHelper', () => { }); it('should give vault tokens to the user', async () => { - expect(await vault.balanceOf(user.address)).to.be.equal(0); + expect(await vaultToken.balanceOf(user.address)).to.be.equal(0); expect(await gauge.balanceOf(user.address)).to.be.equal(0); await vaultHelper.connect(user).depositVault(vault.address, ether('100')); - expect(await vault.balanceOf(user.address)).to.be.equal(ether('100')); + expect(await vaultToken.balanceOf(user.address)).to.be.equal(ether('100')); expect(await gauge.balanceOf(user.address)).to.be.equal(0); }); }); @@ -94,7 +106,7 @@ describe('VaultHelper', () => { it('should withdraw from the gauge and vault', async () => { await vaultHelper.connect(user).withdrawVault(vault.address, ether('100')); expect(await gauge.balanceOf(user.address)).to.be.equal(0); - expect(await vault.balanceOf(user.address)).to.be.equal(0); + expect(await vaultToken.balanceOf(user.address)).to.be.equal(0); expect(await dai.balanceOf(user.address)).to.be.equal(ether('99999999.9')); }); }); @@ -103,13 +115,13 @@ describe('VaultHelper', () => { beforeEach(async () => { await vault.connect(deployer).setGauge(ethers.constants.AddressZero); await vaultHelper.connect(user).depositVault(vault.address, ether('100')); - expect(await vault.balanceOf(user.address)).to.be.equal(ether('100')); + expect(await vaultToken.balanceOf(user.address)).to.be.equal(ether('100')); }); it('should withdraw from the vault', async () => { await vaultHelper.connect(user).withdrawVault(vault.address, ether('100')); expect(await gauge.balanceOf(user.address)).to.be.equal(0); - expect(await vault.balanceOf(user.address)).to.be.equal(0); + expect(await vaultToken.balanceOf(user.address)).to.be.equal(0); expect(await dai.balanceOf(user.address)).to.be.equal(ether('99999999.9')); }); });