Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: chainsec fixes #33

Merged
merged 18 commits into from
Feb 7, 2024
9 changes: 6 additions & 3 deletions ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,26 @@ dependencies:
- name: openzeppelin
github: OpenZeppelin/openzeppelin-contracts
ref: 4.8.2

- name: yearn-vaults
github: yearn/yearn-vaults-v3
ref: v3.0.1
exclude:
- test/
- test/**/*

- name: tokenized-strategy
github: yearn/tokenized-strategy
ref: v3.0.1
contracts_folder: src
exclude:
- test/
- test/**/*

- name: periphery
github: yearn/tokenized-strategy-periphery
ref: master
contracts_folder: src
exclude:
- test/
- test/**/*

solidity:
import_remapping:
Expand Down
58 changes: 46 additions & 12 deletions contracts/Managers/RoleManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ contract RoleManager is Governance2Step {
mapping(address => VaultConfig) public vaultConfig;
/// @notice Mapping of underlying asset, api version and rating to vault.
mapping(address => mapping(string => mapping(uint256 => address)))
public _assetToVault;
internal _assetToVault;

constructor(
address _governance,
Expand Down Expand Up @@ -153,7 +153,7 @@ contract RoleManager is Governance2Step {
)
});

// Security cna set the max debt for strategies to have.
// Security can set the max debt for strategies to have.
_positions[SECURITY] = Position({
holder: _security,
roles: uint96(Roles.MAX_DEBT_MANAGER)
Expand Down Expand Up @@ -393,20 +393,23 @@ contract RoleManager is Governance2Step {
* @param _vault Address of the vault to set up the accountant for.
*/
function _setAccountant(address _vault) internal virtual {
// Temporarily give this contract the ability to set the accountant.
IVault(_vault).add_role(address(this), Roles.ACCOUNTANT_MANAGER);

// Get the current accountant.
address accountant = getPositionHolder(ACCOUNTANT);

// Set the account on the vault.
IVault(_vault).set_accountant(accountant);
// If there is an accountant set.
if (accountant != address(0)) {
// Temporarily give this contract the ability to set the accountant.
IVault(_vault).add_role(address(this), Roles.ACCOUNTANT_MANAGER);

// Take away the role.
IVault(_vault).remove_role(address(this), Roles.ACCOUNTANT_MANAGER);
// Set the account on the vault.
IVault(_vault).set_accountant(accountant);

// Whitelist the vault in the accountant.
HealthCheckAccountant(accountant).addVault(_vault);
// Take away the role.
IVault(_vault).remove_role(address(this), Roles.ACCOUNTANT_MANAGER);

// Whitelist the vault in the accountant.
HealthCheckAccountant(accountant).addVault(_vault);
}
}

/**
Expand Down Expand Up @@ -586,6 +589,29 @@ contract RoleManager is Governance2Step {
emit RemovedVault(_vault);
}

/**
* @notice Removes a specific role(s) for a `_holder` from the `_vaults`.
* @dev Can be used to remove one specific role or multiple.
* @param _vaults Array of vaults to adjust.
* @param _holder Address who's having a role removed.
* @param _role The role or roles to remove from the `_holder`.
*/
function removeRoles(
address[] calldata _vaults,
address _holder,
uint256 _role
) external virtual onlyGovernance {
address _vault;
for (uint256 i = 0; i < _vaults.length; ++i) {
_vault = _vaults[i];
// Make sure the vault is added to this Role Manager.
require(vaultConfig[_vault].asset != address(0), "vault not added");

// Remove the role.
IVault(_vault).remove_role(_holder, _role);
}
}

/*//////////////////////////////////////////////////////////////
SETTERS
//////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -799,7 +825,15 @@ contract RoleManager is Governance2Step {
}

/**
* @notice Get the address assigned to the allocator.
* @notice Get the address assigned to be the debt allocator if any.
* @return The address assigned to be the debt allocator if any.
*/
function getDebtAllocator() external view virtual returns (address) {
return getPositionHolder(DEBT_ALLOCATOR);
}

/**
* @notice Get the address assigned to the allocator factory.
* @return The address assigned to the allocator factory.
*/
function getAllocatorFactory() external view virtual returns (address) {
Expand Down
77 changes: 76 additions & 1 deletion contracts/Mocks/MockTokenizedStrategy.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,82 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.18;

import {MockTokenizedStrategy} from "@yearn-vaults/test/mocks/ERC4626/MockTokenizedStrategy.sol";
import {TokenizedStrategy, ERC20} from "@tokenized-strategy/TokenizedStrategy.sol";

contract MockTokenizedStrategy is TokenizedStrategy {
uint256 public minDebt;
uint256 public maxDebt = type(uint256).max;

// Private variables and functions used in this mock.
bytes32 public constant BASE_STRATEGY_STORAGE =
bytes32(uint256(keccak256("yearn.base.strategy.storage")) - 1);

function strategyStorage() internal pure returns (StrategyData storage S) {
// Since STORAGE_SLOT is a constant, we have to put a variable
// on the stack to access it from an inline assembly block.
bytes32 slot = BASE_STRATEGY_STORAGE;
assembly {
S.slot := slot
}
}

constructor(
address _asset,
string memory _name,
address _management,
address _keeper
) {
// Cache storage pointer
StrategyData storage S = strategyStorage();

// Set the strategy's underlying asset
S.asset = ERC20(_asset);
// Set the Strategy Tokens name.
S.name = _name;
// Set decimals based off the `asset`.
S.decimals = ERC20(_asset).decimals();

// Set last report to this block.
S.lastReport = uint128(block.timestamp);

// Set the default management address. Can't be 0.
require(_management != address(0), "ZERO ADDRESS");
S.management = _management;
S.performanceFeeRecipient = _management;
// Set the keeper address
S.keeper = _keeper;
}

function setMinDebt(uint256 _minDebt) external {
minDebt = _minDebt;
}

function setMaxDebt(uint256 _maxDebt) external {
maxDebt = _maxDebt;
}

function availableDepositLimit(
address
) public view virtual returns (uint256) {
uint256 _totalAssets = strategyStorage().totalIdle;
uint256 _maxDebt = maxDebt;
return _maxDebt > _totalAssets ? _maxDebt - _totalAssets : 0;
}

function availableWithdrawLimit(
address /*_owner*/
) public view virtual returns (uint256) {
return type(uint256).max;
}

function deployFunds(uint256 _amount) external virtual {}

function freeFunds(uint256 _amount) external virtual {}

function harvestAndReport() external virtual returns (uint256) {
return strategyStorage().asset.balanceOf(address(this));
}
}

contract MockTokenized is MockTokenizedStrategy {
uint256 public apr;
Expand Down
69 changes: 39 additions & 30 deletions contracts/accountants/HealthCheckAccountant.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ contract HealthCheckAccountant {
_;
}

modifier onlyVaultOrFeeManagers() {
modifier onlyVaultOrFeeManager() {
_checkVaultOrFeeManager();
_;
}
Expand Down Expand Up @@ -151,29 +151,18 @@ contract HealthCheckAccountant {
) {
require(_feeManager != address(0), "ZERO ADDRESS");
require(_feeRecipient != address(0), "ZERO ADDRESS");
require(
defaultManagement <= MANAGEMENT_FEE_THRESHOLD,
"exceeds management fee threshold"
);
require(
defaultPerformance <= PERFORMANCE_FEE_THRESHOLD,
"exceeds performance fee threshold"
);
require(defaultMaxLoss <= MAX_BPS, "too high");

feeManager = _feeManager;
feeRecipient = _feeRecipient;

defaultConfig = Fee({
managementFee: defaultManagement,
performanceFee: defaultPerformance,
refundRatio: defaultRefund,
maxFee: defaultMaxFee,
maxGain: defaultMaxGain,
maxLoss: defaultMaxLoss
});

emit UpdateDefaultFeeConfig(defaultConfig);
_updateDefaultConfig(
defaultManagement,
defaultPerformance,
defaultRefund,
defaultMaxFee,
defaultMaxGain,
defaultMaxLoss
);
}

/**
Expand Down Expand Up @@ -275,7 +264,7 @@ contract HealthCheckAccountant {
* @dev This is not used to set any of the fees for the specific vault or strategy. Each fee will be set separately.
* @param vault The address of a vault to allow to use this accountant.
*/
function addVault(address vault) external virtual onlyVaultOrFeeManagers {
function addVault(address vault) external virtual onlyVaultOrFeeManager {
// Ensure the vault has not already been added.
require(!vaults[vault], "already added");

Expand All @@ -288,9 +277,7 @@ contract HealthCheckAccountant {
* @notice Function to remove a vault from this accountant's fee charging list.
* @param vault The address of the vault to be removed from this accountant.
*/
function removeVault(
address vault
) external virtual onlyVaultOrFeeManagers {
function removeVault(address vault) external virtual onlyVaultOrFeeManager {
// Ensure the vault has been previously added.
require(vaults[vault], "not added");

Expand Down Expand Up @@ -323,14 +310,36 @@ contract HealthCheckAccountant {
uint16 defaultMaxGain,
uint16 defaultMaxLoss
) external virtual onlyFeeManager {
_updateDefaultConfig(
defaultManagement,
defaultPerformance,
defaultRefund,
defaultMaxFee,
defaultMaxGain,
defaultMaxLoss
);
}

/**
* @dev Updates the Accountant's default fee config.
* Is used during deployment and during any future updates.
*/
function _updateDefaultConfig(
uint16 defaultManagement,
uint16 defaultPerformance,
uint16 defaultRefund,
uint16 defaultMaxFee,
uint16 defaultMaxGain,
uint16 defaultMaxLoss
) internal virtual {
// Check for threshold and limit conditions.
require(
defaultManagement <= MANAGEMENT_FEE_THRESHOLD,
"exceeds management fee threshold"
"management fee threshold"
);
require(
defaultPerformance <= PERFORMANCE_FEE_THRESHOLD,
"exceeds performance fee threshold"
"performance fee threshold"
);
require(defaultMaxLoss <= MAX_BPS, "too high");

Expand Down Expand Up @@ -373,11 +382,11 @@ contract HealthCheckAccountant {
// Check for threshold and limit conditions.
require(
customManagement <= MANAGEMENT_FEE_THRESHOLD,
"exceeds management fee threshold"
"management fee threshold"
);
require(
customPerformance <= PERFORMANCE_FEE_THRESHOLD,
"exceeds performance fee threshold"
"performance fee threshold"
);
require(customMaxLoss <= MAX_BPS, "too high");

Expand Down Expand Up @@ -583,8 +592,8 @@ contract HealthCheckAccountant {
uint256 _amount
) internal {
if (ERC20(_token).allowance(address(this), _contract) < _amount) {
ERC20(_token).approve(_contract, 0);
ERC20(_token).approve(_contract, _amount);
ERC20(_token).safeApprove(_contract, 0);
ERC20(_token).safeApprove(_contract, _amount);
}
}

Expand Down
Loading
Loading