#45910 [SC-Medium] Changing collateral ratio makes Agents prone to liquidation
Submitted on May 22nd 2025 at 11:47:40 UTC by @pseudoArtist for Audit Comp | Flare | FAssets
Report ID: #45910
Report Type: Smart Contract
Report severity: Medium
Target: https://github.com/flare-foundation/fassets/blob/main/contracts/assetManager/library/Liquidation.sol
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
setCollateralRatiosForToken
when called changes the important ratios for the collateral and when this is called without any prior information and done infavourably will make positions of the Agents unhealthy and prone to liquidation, There should be some grace time given to implement the changes like 24 hours such that Agents can get sufficient time to deposit more collateral to make there position healthy and above min liquidation threshold.
Vulnerability Details
function setCollateralRatiosForToken(
CollateralType.Class _collateralClass,
IERC20 _token,
uint256 _minCollateralRatioBIPS,
uint256 _ccbMinCollateralRatioBIPS,
uint256 _safetyMinCollateralRatioBIPS
)
external
onlyAssetManagerController
{
CollateralTypes.setCollateralRatios(_collateralClass, _token,
_minCollateralRatioBIPS, _ccbMinCollateralRatioBIPS, _safetyMinCollateralRatioBIPS);
}
The function setCollateralRatiosForToken
is called from the AssetManagerController
to change the important ratios like _minCollateralRatioBIPS
, _ccbMinCollateralRatioBIPS
and _safetyMinCollateralRatioBIPS
, It does so by calling setCollateralRatios
on the CollateralTypes
Library.
function setCollateralRatios(
CollateralType.Class _collateralClass,
IERC20 _token,
uint256 _minCollateralRatioBIPS,
uint256 _ccbMinCollateralRatioBIPS,
uint256 _safetyMinCollateralRatioBIPS
)
internal
{
bool ratiosValid =
SafePct.MAX_BIPS < _ccbMinCollateralRatioBIPS &&
_ccbMinCollateralRatioBIPS <= _minCollateralRatioBIPS &&
_minCollateralRatioBIPS <= _safetyMinCollateralRatioBIPS;
require(ratiosValid, "invalid collateral ratios");
// update
CollateralTypeInt.Data storage token = CollateralTypes.get(_collateralClass, _token);
token.minCollateralRatioBIPS = _minCollateralRatioBIPS.toUint32();
token.ccbMinCollateralRatioBIPS = _ccbMinCollateralRatioBIPS.toUint32();
token.safetyMinCollateralRatioBIPS = _safetyMinCollateralRatioBIPS.toUint32();
emit IAssetManagerEvents.CollateralRatiosChanged(uint8(_collateralClass), address(_token),
_minCollateralRatioBIPS, _ccbMinCollateralRatioBIPS, _safetyMinCollateralRatioBIPS);
}
The only check it has is of ratioValid
and if the ratios are set in such a way that it is valid it can be set easily. However while doing so the Agents face a serious consequence as some of the agent's positions which are borderline healthy or just above the liquidation threshold can become unhealthy and prone to liquidation.
startLiquidation
--> _upgradeLiquidationPhase
--> _initialLiquidationPhaseForCollateral
function _initialLiquidationPhaseForCollateral(
uint256 _collateralRatioBIPS,
uint256 _collateralIndex
)
private view
returns (Agent.LiquidationPhase)
{
AssetManagerState.State storage state = AssetManagerState.get();
CollateralTypeInt.Data storage collateral = state.collateralTokens[_collateralIndex];
if (_collateralRatioBIPS >= collateral.minCollateralRatioBIPS) {
return Agent.LiquidationPhase.NONE;
} else if (_collateralRatioBIPS >= collateral.ccbMinCollateralRatioBIPS) {
return Agent.LiquidationPhase.CCB;
} else {
return Agent.LiquidationPhase.LIQUIDATION;
}
}
The call to _initialLiquidationPhaseForCollateral
returns the LiquidationPhase, and upon updating the minCollateralRatioBIPS
the position which was healthy will become unhealthy at once and will fall into LIQUIDATION
phase and become liquidated immediately.
Impact Details
Healthy positions of Agents will become prone to liquidation
References
function setCollateralRatios(
CollateralType.Class _collateralClass,
IERC20 _token,
uint256 _minCollateralRatioBIPS,
uint256 _ccbMinCollateralRatioBIPS,
uint256 _safetyMinCollateralRatioBIPS
)
internal
{
bool ratiosValid =
SafePct.MAX_BIPS < _ccbMinCollateralRatioBIPS &&
_ccbMinCollateralRatioBIPS <= _minCollateralRatioBIPS &&
_minCollateralRatioBIPS <= _safetyMinCollateralRatioBIPS;
require(ratiosValid, "invalid collateral ratios");
// update
CollateralTypeInt.Data storage token = CollateralTypes.get(_collateralClass, _token);
token.minCollateralRatioBIPS = _minCollateralRatioBIPS.toUint32();
token.ccbMinCollateralRatioBIPS = _ccbMinCollateralRatioBIPS.toUint32();
token.safetyMinCollateralRatioBIPS = _safetyMinCollateralRatioBIPS.toUint32();
emit IAssetManagerEvents.CollateralRatiosChanged(uint8(_collateralClass), address(_token),
_minCollateralRatioBIPS, _ccbMinCollateralRatioBIPS, _safetyMinCollateralRatioBIPS);
}
function _initialLiquidationPhaseForCollateral(
uint256 _collateralRatioBIPS,
uint256 _collateralIndex
)
private view
returns (Agent.LiquidationPhase)
{
AssetManagerState.State storage state = AssetManagerState.get();
CollateralTypeInt.Data storage collateral = state.collateralTokens[_collateralIndex];
if (_collateralRatioBIPS >= collateral.minCollateralRatioBIPS) {
return Agent.LiquidationPhase.NONE;
} else if (_collateralRatioBIPS >= collateral.ccbMinCollateralRatioBIPS) {
return Agent.LiquidationPhase.CCB;
} else {
return Agent.LiquidationPhase.LIQUIDATION;
}
}
Proof of Concept
Proof of Concept
Note that this change can also be non deliberate but will result in the same issue.
An agent is barely healthy under old ratios (e.g., 151% with old minCollateralRatioBIPS = 150%).
The admin updates
minCollateralRatioBIPS
to 155%.The agent’s ratio (151%) is now below the new threshold (155%) → Agent is moved to LIQUIDATION.
Before Update : minCollateralRatioBIPS: 150%
Agent A:
Collateral: 1000 C1 tokens. , Debt: 600 f-assets.
Collateral Ratio: (1000 C1 / 600 f-assets) * 100 = 166.67%.
minCollateralRatioBIPS: 150% → Healthy (NORMAL).
After Update (minCollateralRatioBIPS → 160%)
Agent A:
Collateral ratio (166.67%) is still above 160% → Remains healthy.
Agent B:
Collateral: 900 C1 tokens.
Debt: 600 f-assets.
Collateral Ratio: (900 / 600) * 100 = 150%.
Now below new `minCollateralRatioBIPS` (160%) → Enters LIQUIDATION.
Was this helpful?