Skip to content

Commit 6240aaa

Browse files
authored
adding the case for the dust liquidation (#187)
* adding the case for the dust liquidation * adding an upgradeability check on the debt * changing comments and tests here and there
1 parent f80fe1d commit 6240aaa

12 files changed

Lines changed: 307 additions & 133 deletions

File tree

contracts/deprecated/vaultManager/OldVaultManager.sol

Lines changed: 113 additions & 69 deletions
Large diffs are not rendered by default.

contracts/deprecated/vaultManager/OldVaultManagerStorage.sol

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,6 @@ contract OldVaultManagerStorage is IVaultManagerStorage, Initializable, Reentran
5050
// ================================= PARAMETERS ================================
5151
// Unless specified otherwise, parameters of this contract are expressed in `BASE_PARAMS`
5252

53-
uint256 public immutable dust;
54-
/// @notice Minimum amount of collateral (in stablecoin value, e.g in `BASE_TOKENS = 10**18`) that can be left
55-
/// in a vault during a liquidation where the health factor function is decreasing
56-
uint256 internal immutable _dustCollateral;
5753
/// @notice Maximum amount of stablecoins that can be issued with this contract (in `BASE_TOKENS`). This parameter should
5854
/// not be bigger than `type(uint256).max / BASE_INTEREST` otherwise there may be some overflows in the `increaseDebt` function
5955
uint256 public debtCeiling;
@@ -167,12 +163,6 @@ contract OldVaultManagerStorage is IVaultManagerStorage, Initializable, Reentran
167163
error TooSmallParameterValue();
168164
error ZeroAddress();
169165

170-
/// @param _dust Minimum amount of debt a vault from this implementation can have
171-
/// @param dustCollateral_ Minimum amount of collateral (in stablecoin value) that can be left in a vault during a liquidation
172-
/// where the health factor function is decreasing
173-
/// @dev Run only at the implementation level
174-
constructor(uint256 _dust, uint256 dustCollateral_) initializer {
175-
dust = _dust;
176-
_dustCollateral = dustCollateral_;
177-
}
166+
/// @custom:oz-upgrades-unsafe-allow constructor
167+
constructor() initializer {}
178168
}

contracts/vaultManager/VaultManager.sol

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
5454
/// in a vault during a liquidation where the health factor function is decreasing
5555
uint256 internal _dustCollateral;
5656

57-
uint256[48] private __gapVaultManager;
57+
/// @notice If the amount of debt of a vault that gets liquidated is below this amount, then the liquidator
58+
/// can liquidate all the debt of the vault (and not just what's needed to get to the target health factor)
59+
uint256 public dustLiquidation;
60+
61+
uint256[47] private __gapVaultManager;
5862

5963
/// @inheritdoc IVaultManagerFunctions
6064
function initialize(
@@ -719,6 +723,7 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
719723
// Because we're rounding up in some divisions, `collateralReleased` can be greater than the `collateralAmount` of the vault
720724
// In this case, `stablecoinAmountToReceive` is still rounded up
721725
if (vault.collateralAmount <= collateralReleased) {
726+
// Liquidators should never get more collateral than what's in the vault
722727
collateralReleased = vault.collateralAmount;
723728
// Remove all the vault's debt (debt repayed + bad debt) from VaultManager totalDebt
724729
totalNormalizedDebt -= vault.normalizedDebt;
@@ -778,12 +783,11 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
778783
: BASE_PARAMS - liquidationDiscount;
779784
// Same for the surcharge here: it's in fact 1 - the fee taken by the protocol
780785
uint256 surcharge = liquidationSurcharge;
781-
// Checking if we're in a situation where the health factor is an increasing or a decreasing function of the
782-
// amount repaid
783786
uint256 maxAmountToRepay;
784787
uint256 thresholdRepayAmount;
785-
// In the first case, the health factor is an increasing function of the stablecoin amount to repay,
786-
// this means that the liquidator can bring the vault to the target health ratio
788+
// Checking if we're in a situation where the health factor is an increasing or a decreasing function of the
789+
// amount repaid. In the first case, the health factor is an increasing function which means that the liquidator
790+
// can bring the vault to the target health ratio
787791
if (healthFactor * liquidationDiscount * surcharge >= collateralFactor * BASE_PARAMS**2) {
788792
// This is the max amount to repay that will bring the person to the target health factor
789793
// Denom is always positive when a vault gets liquidated in this case and when the health factor
@@ -794,32 +798,27 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
794798
BASE_PARAMS *
795799
liquidationDiscount) /
796800
(surcharge * targetHealthFactor * liquidationDiscount - (BASE_PARAMS**2) * collateralFactor);
797-
// The quantity below tends to be rounded in the above direction, which means that governance or guardians should
798-
// set the `targetHealthFactor` accordingly
799-
// Need to check for the dust: liquidating should not leave a dusty amount in the vault
800-
if (currentDebt * BASE_PARAMS <= maxAmountToRepay * surcharge + dust * BASE_PARAMS) {
801-
// If liquidating to the target threshold would leave a dusty amount: the liquidator can repay all
802-
// We're rounding up the max amount to repay to make sure all the debt ends up being paid
803-
// and we're computing again the real value of the debt to avoid propagation of rounding errors
801+
// Need to check for the dustas liquidating should not leave a dusty amount in the vault
802+
uint256 dustParameter = dustLiquidation;
803+
if (currentDebt * BASE_PARAMS <= maxAmountToRepay * surcharge + dustParameter * BASE_PARAMS) {
804+
// If liquidating to the target threshold would leave a dusty amount: the liquidator can repay all.
805+
// We're avoiding here propagation of rounding errors and rounding up the max amount to repay to make
806+
// sure all the debt ends up being paid
804807
maxAmountToRepay =
805808
(vault.normalizedDebt * newInterestAccumulator * BASE_PARAMS) /
806809
(surcharge * BASE_INTEREST) +
807810
1;
808811
// In this case the threshold amount is such that it leaves just enough dust: amount is rounded
809-
// down such that if a liquidator repays this amount then there would be more than `dust` left in
812+
// down such that if a liquidator repays this amount then there is more than `dustLiquidation` left in
810813
// the liquidated vault
811-
if (currentDebt > dust)
812-
thresholdRepayAmount = ((currentDebt - dust) * BASE_PARAMS) / surcharge;
813-
// If there is from the beginning a dusty debt (because of an implementation upgrade), then
814-
// liquidator should repay everything that's left
814+
if (currentDebt > dustParameter)
815+
thresholdRepayAmount = ((currentDebt - dustParameter) * BASE_PARAMS) / surcharge;
816+
// If there is from the beginning a dusty debt, then liquidator should repay everything that's left
815817
else thresholdRepayAmount = 1;
816818
}
817819
} else {
818-
// In all cases the liquidator can repay stablecoins such that they'll end up getting exactly the collateral
820+
// In this case, the liquidator can repay stablecoins such that they'll end up getting exactly the collateral
819821
// in the liquidated vault
820-
// Rounding up to make sure all gets liquidated in this case: the liquidator will never get more than the collateral
821-
// amount in the vault however: we're performing the computation of the `collateralAmountInStable` again to avoid
822-
// propagation of rounding errors
823822
maxAmountToRepay =
824823
(vault.collateralAmount * liquidationDiscount * oracleValue) /
825824
(BASE_PARAMS * _collatBase) +
@@ -864,7 +863,7 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
864863
borrowFee = param;
865864
} else if (what == "RF") {
866865
// As liquidation surcharge is stored as `1-fee` and as we need `repayFee` to be smaller
867-
// then the liquidation surcharge, then we need to have:
866+
// than the liquidation surcharge, we need to have:
868867
// `liquidationSurcharge <= BASE_PARAMS - repayFee` and as such `liquidationSurcharge + repayFee <= BASE_PARAMS`
869868
if (param + liquidationSurcharge > BASE_PARAMS) revert TooHighParameterValue();
870869
repayFee = param;
@@ -933,10 +932,17 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
933932

934933
/// @notice Sets the dust variables
935934
/// @param _dust New minimum debt allowed
935+
/// @param _dustLiquidation New `dustLiquidation` value
936936
/// @param dustCollateral_ New minimum collateral allowed in a vault after a liquidation
937937
/// @dev dustCollateral_ is in stable value
938-
function setDusts(uint256 _dust, uint256 dustCollateral_) external onlyGovernor {
938+
function setDusts(
939+
uint256 _dust,
940+
uint256 _dustLiquidation,
941+
uint256 dustCollateral_
942+
) external onlyGovernor {
943+
if (_dust > _dustLiquidation) revert InvalidParameterValue();
939944
dust = _dust;
945+
dustLiquidation = _dustLiquidation;
940946
_dustCollateral = dustCollateral_;
941947
}
942948

deploy/5_vaultManagerImplementation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ const func: DeployFunction = async ({ deployments, ethers }) => {
77
const { deployer } = await ethers.getNamedSigners();
88

99
console.log('Now deploying the implementation for VaultManager');
10-
await deploy('VaultManager_Implementation', {
10+
await deploy('VaultManager_V2_0_Implementation', {
1111
contract: 'VaultManagerLiquidationBoost',
1212
from: deployer.address,
1313
args: [],
1414
log: !argv.ci,
1515
});
1616

17-
const vaultManagerImplementation = (await ethers.getContract('VaultManager_Implementation')).address;
17+
const vaultManagerImplementation = (await ethers.getContract('VaultManager_V2_0_Implementation')).address;
1818

1919
console.log(`Successfully deployed the implementation for VaultManager at ${vaultManagerImplementation}`);
2020
console.log('');

hardhat.config.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,10 @@ const config: HardhatUserConfig = {
143143
forking: {
144144
enabled: argv.fork || false,
145145
// Mainnet
146-
/*
146+
147147
url: nodeUrl('fork'),
148-
blockNumber: 15975107,
149-
*/
148+
blockNumber: 16218353,
149+
150150
// Polygon
151151
/*
152152
url: nodeUrl('forkpolygon'),
@@ -164,9 +164,10 @@ const config: HardhatUserConfig = {
164164
blockNumber: 19356874,
165165
*/
166166
// Avalanche
167-
167+
/*
168168
url: nodeUrl('avalanche'),
169169
blockNumber: 23545788,
170+
*/
170171
},
171172
mining: argv.disableAutoMining
172173
? {

scripts/mainnet-fork/upgradeVaultManager.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ async function main() {
1414

1515
const vaultManagerAddress = '0x73aaf8694BA137a7537E7EF544fcf5E2475f227B';
1616

17-
const implementation = (await deployments.get('VaultManagerNoDust_Implementation')).address;
17+
const implementation = (await deployments.get('VaultManager_V2_0_Implementation')).address;
18+
19+
console.log(implementation);
1820

1921
const proxyAdminAddress = CONTRACTS_ADDRESSES[ChainId.MAINNET].ProxyAdmin!;
2022

@@ -58,8 +60,9 @@ async function main() {
5860
console.log(await vaultManager.name());
5961
console.log(await vaultManager.symbol());
6062

61-
await vaultManager.connect(signer).setDusts(10, 10);
63+
await vaultManager.connect(signer).setDusts(10, 10, 10);
6264
console.log((await vaultManager.dust()).toString());
65+
console.log((await vaultManager.dustLiquidation()).toString());
6366

6467
console.log((await vaultManager.vaultData(8)).collateralAmount.toString());
6568
console.log((await vaultManager.vaultData(8)).normalizedDebt.toString());

test/hardhat/reactor/reactor.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ contract('Reactor', () => {
122122
stableMaster = await new MockStableMaster__factory(deployer).deploy();
123123

124124
await vaultManager.initialize(treasury.address, ANGLE.address, oracle.address, params, 'USDC/agEUR');
125-
await vaultManager.connect(governor).setDusts(0.1e15, 0.1e15);
125+
await vaultManager.connect(governor).setDusts(0.1e15, 0.1e15, 0.1e15);
126126
await vaultManager.connect(guardian).togglePause();
127127

128128
reactor = (await deployUpgradeable(new Reactor__factory(deployer))) as Reactor;

test/hardhat/vaultManager/vaultManager.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ contract('VaultManagerLiquidationBoost', () => {
130130
await vaultManager.initialize(treasury.address, collateral.address, oracle.address, params, 'USDC/agEUR');
131131
await vaultManager.connect(guardian).togglePause();
132132
await vaultManager.connect(governor).setUint64(params.borrowFee, formatBytes32String('BF'));
133-
await vaultManager.connect(governor).setDusts(0.1e9, 0.1e9);
133+
await vaultManager.connect(governor).setDusts(0.1e9, 0.1e9, 0.1e9);
134134
});
135135
describe('oracle', () => {
136136
it('success - read', async () => {
@@ -1178,7 +1178,7 @@ contract('VaultManagerLiquidationBoost', () => {
11781178
await treasury.addMinter(agToken.address, vaultManager.address);
11791179
oracle = await new MockOracle__factory(deployer).deploy(parseUnits('2', 18), treasury.address);
11801180
await vaultManager.initialize(treasury.address, agToken.address, oracle.address, params, 'USDC/agEUR');
1181-
await vaultManager.connect(governor).setDusts(0.1e9, 0.1e9);
1181+
await vaultManager.connect(governor).setDusts(0.1e9, 0.1e9, 0.1e9);
11821182
await vaultManager.connect(guardian).togglePause();
11831183
});
11841184
it('success - allowance given', async () => {
@@ -2263,7 +2263,7 @@ contract('VaultManagerLiquidationBoost', () => {
22632263
params.interestRate = parseEther('0');
22642264
params.borrowFee = 0;
22652265
await vaultManager.initialize(treasury.address, collateral.address, oracle.address, params, 'USDC/agEUR');
2266-
await vaultManager.connect(governor).setDusts(parseEther('0.5'), parseEther('0.5'));
2266+
await vaultManager.connect(governor).setDusts(parseEther('0.5'), parseEther('0.5'), parseEther('0.5'));
22672267
await vaultManager.connect(guardian).togglePause();
22682268
await treasury.setVaultManager2(vaultManager.address);
22692269
await treasury.addMinter(agToken.address, vaultManager.address);
@@ -2289,7 +2289,7 @@ contract('VaultManagerLiquidationBoost', () => {
22892289
params.interestRate = parseEther('0');
22902290
params.borrowFee = 0;
22912291
await vaultManager.initialize(treasury.address, collateral.address, oracle.address, params, 'USDC/agEUR');
2292-
await vaultManager.connect(governor).setDusts(parseEther('0.5'), parseEther('0.5'));
2292+
await vaultManager.connect(governor).setDusts(parseEther('0.5'), parseEther('0.5'), parseEther('0.5'));
22932293
await vaultManager.connect(guardian).togglePause();
22942294
await treasury.setVaultManager2(vaultManager.address);
22952295
await treasury.addMinter(agToken.address, vaultManager.address);

0 commit comments

Comments
 (0)