Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/controller/VaultManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ abstract contract VaultManager is BaseController, PriceFeedManager {
* @notice Thrown when trying to set a vault as the main vault for an asset it already is
*/
error Vault_AlreadyMainVaultForAsset();
/**
* @notice Thrown when the overall vaults settings are invalid
*/
error Vault_InvalidVaultsSettings();

/**
* @notice Initializes the VaultManager with an empty vaults linked list
Expand Down Expand Up @@ -121,6 +125,7 @@ abstract contract VaultManager is BaseController, PriceFeedManager {
_checkVaultSettings(settings);
vaultSettings[vault] = settings;
emit VaultSettingsUpdated(vault, settings.maxCapacity, settings.maxProportionality, settings.minProportionality);
_checkVaultsProportionalities();

if (isMainVaultForAsset || _vaultFor[asset] == address(0)) {
emit MainVaultForAssetUpdated(asset, _vaultFor[asset], vault);
Expand All @@ -146,6 +151,7 @@ abstract contract VaultManager is BaseController, PriceFeedManager {

delete vaultSettings[vault];
emit VaultSettingsUpdated(vault, 0, 0, 0);
_checkVaultsProportionalities();

address asset = IControlledVault(vault).asset();
if (_vaultFor[asset] == vault) {
Expand Down Expand Up @@ -173,6 +179,7 @@ abstract contract VaultManager is BaseController, PriceFeedManager {
_checkVaultSettings(settings);
vaultSettings[vault] = settings;
emit VaultSettingsUpdated(vault, settings.maxCapacity, settings.maxProportionality, settings.minProportionality);
_checkVaultsProportionalities();
}

/**
Expand Down Expand Up @@ -234,6 +241,34 @@ abstract contract VaultManager is BaseController, PriceFeedManager {
require(settings.minProportionality <= settings.maxProportionality, Vault_MinProportionalityNotLessThanMax());
}

/**
* @dev Validates that the sum of any two vaults' maximum proportionalities is greater than or equal to 100%.
* This ensures that the protocol cannot get into a deadlock due to proportionality constraints.
*/
function _checkVaultsProportionalities() internal view {
address[] memory allVaults = vaults();
uint256 count = allVaults.length;
if (count == 0) return; // No vaults to check
if (count == 1) {
// Single vault must be allowed to cover 100% of the assets
require(vaultSettings[allVaults[0]].maxProportionality == MAX_BPS, Vault_InvalidVaultsSettings());
} else if (count == 2) {
// Sum of both vaults' max proportionalities must be at least 120%
uint256 sum =
vaultSettings[allVaults[0]].maxProportionality + vaultSettings[allVaults[1]].maxProportionality;
require(sum >= 12_000, Vault_InvalidVaultsSettings());
} else {
// For 3 or more vaults, ensure that the sum of the two smallest max proportionalities is at least 100%
uint256 maxProportionality;
uint256 minMaxProportionality = MAX_BPS;
for (uint256 i; i < count; ++i) {
maxProportionality = vaultSettings[allVaults[i]].maxProportionality;
require(maxProportionality + minMaxProportionality >= MAX_BPS, Vault_InvalidVaultsSettings());
minMaxProportionality = minMaxProportionality.min(maxProportionality);
}
}
}

/**
* @notice Retrieves the asset managed by a given vault
* @param vault The address of the vault
Expand Down
4 changes: 4 additions & 0 deletions tests/harness/ControllerHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ contract ControllerHarness is Controller {
return _vaultsOverview(calculateTotalValue);
}

function exposed_checkVaultsProportionalities() external view {
_checkVaultsProportionalities();
}

function exposed_maxDepositLimit(address vault) external view returns (uint256 max) {
return _maxDepositLimit(_vaultsOverview(false), vault);
}
Expand Down
Loading