Vault will remain insolvent after liquidation, because _amountToSolvency that is being calculated is not sufficient enough to keep the vault solvent after liquidation has been concluded.
Proof of Concept
// calculate the amount to liquidate and the 'bad fill price' using liquidationMath
// see _liquidationMath for more detailed explaination of the math
(uint256 _tokenAmount, uint256 _badFillPrice) = _liquidationMath(_id, _assetAddress, _tokensToLiquidate);
// set _tokensToLiquidate to this calculated amount if the function does not fail
if (_tokenAmount > 0) _tokensToLiquidate = _tokenAmount;
// the USDA to repurchase is equal to the bad fill price multiplied by the amount of tokens to liquidate
The statement in the snippet below is not true. The reason is after the liquidation of that amount of token based on the protocol calculation, the vault will still be insolvent. Instance will be explained in detail;
// the maximum amount of tokens to liquidate is the amount that will bring the vault to solvency
// divided by the denominator
uint256 _maxTokensToLiquidate = (_usdaToSolvency * 1e18) / _denominator;
Follow a scenario where a vault with $100 worth of asset decides to take a loan with an 80% ltv.
Loan of $80(max obtainable based on 80% ltv) worth was obtained by the vault.
Asset dropped to $90 based on market condition, making the vault insolvent, as the current loan is now above the borrowPower of the vault.
A liquidator calls the liquidateVault method to liquidate the vault.
This above snippet is where the problem lies, in this case, _vaultLiability(_id) is $80, while the current borowPower based on $90 asset value and 80% ltv, is $72.
Following the usdaToSolvency calculation it will be 80 - 72 = 8
Following the liquidation incentive (5% in this case), the liquidator will pay $8 debt to liquidate $8.4 worth of asset.
After the liquidation that amount, the vault will still be insolvent because debt will be 80 - 8 = 72, while asset will be reduced to 90 - 8.4 = 81.6
Following the requirement for solvency that current lown must me less or equal to max borrowing power of the vault, if the current asset value is 81.6, the max borrow power is 0.8 * 81.6 = 65.28 which is a value lower than than the current liability/loan of 72.
Tools Used
Manual review
Recommended Mitigation Steps
Ideally the maximum to make the vault solvent is the entire loan, in this case the protocol was try to get the minimum which still does not make the vault solvent after liquidation of the calculated amount.
I would recommend the protocol allows a max of 70% vault loan repayment for equivalent amount of the asset value in tokens, based on calculations, liquidating 70% repayment of loan by liquidator will leave the vault solvent after vault asset is reduced by liquidation.
Lines of code
https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L831-L833 https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L846-L851 https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L866-L888 https://github.com/code-423n4/2023-07-amphora/blob/daae020331404647c661ab534d20093c875483e1/core/solidity/contracts/core/VaultController.sol#L768-L810
Vulnerability details
Impact
Vault will remain insolvent after liquidation, because
_amountToSolvency
that is being calculated is not sufficient enough to keep the vault solvent after liquidation has been concluded.Proof of Concept
The statement in the snippet below is not true. The reason is after the liquidation of that amount of token based on the protocol calculation, the vault will still be insolvent. Instance will be explained in detail;
Follow a scenario where a vault with $100 worth of asset decides to take a loan with an 80% ltv.
Loan of $80(max obtainable based on 80% ltv) worth was obtained by the vault.
Asset dropped to $90 based on market condition, making the vault insolvent, as the current loan is now above the borrowPower of the vault.
A liquidator calls the
liquidateVault
method to liquidate the vault.This above snippet is where the problem lies, in this case, _vaultLiability(_id) is $80, while the current borowPower based on $90 asset value and 80% ltv, is $72.
Following the usdaToSolvency calculation it will be
80 - 72 = 8
Following the liquidation incentive (5% in this case), the liquidator will pay $8 debt to liquidate $8.4 worth of asset.
After the liquidation that amount, the vault will still be insolvent because debt will be
80 - 8 = 72
, while asset will be reduced to90 - 8.4 = 81.6
Following the requirement for solvency that current lown must me less or equal to max borrowing power of the vault, if the current asset value is 81.6, the max borrow power is
0.8 * 81.6 = 65.28
which is a value lower than than the current liability/loan of 72.Tools Used
Manual review
Recommended Mitigation Steps
Ideally the maximum to make the vault solvent is the entire loan, in this case the protocol was try to get the minimum which still does not make the vault solvent after liquidation of the calculated amount.
I would recommend the protocol allows a max of 70% vault loan repayment for equivalent amount of the asset value in tokens, based on calculations, liquidating 70% repayment of loan by liquidator will leave the vault solvent after vault asset is reduced by liquidation.
Assessed type
Math