Skip to content

Commit

Permalink
adding the case for the dust liquidation (#187)
Browse files Browse the repository at this point in the history
* adding the case for the dust liquidation

* adding an upgradeability check on the debt

* changing comments and tests here and there
  • Loading branch information
sogipec authored Dec 19, 2022
1 parent f80fe1d commit 6240aaa
Show file tree
Hide file tree
Showing 12 changed files with 307 additions and 133 deletions.
182 changes: 113 additions & 69 deletions contracts/deprecated/vaultManager/OldVaultManager.sol

Large diffs are not rendered by default.

14 changes: 2 additions & 12 deletions contracts/deprecated/vaultManager/OldVaultManagerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ contract OldVaultManagerStorage is IVaultManagerStorage, Initializable, Reentran
// ================================= PARAMETERS ================================
// Unless specified otherwise, parameters of this contract are expressed in `BASE_PARAMS`

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

/// @param _dust Minimum amount of debt a vault from this implementation can have
/// @param dustCollateral_ Minimum amount of collateral (in stablecoin value) that can be left in a vault during a liquidation
/// where the health factor function is decreasing
/// @dev Run only at the implementation level
constructor(uint256 _dust, uint256 dustCollateral_) initializer {
dust = _dust;
_dustCollateral = dustCollateral_;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() initializer {}
}
52 changes: 29 additions & 23 deletions contracts/vaultManager/VaultManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
/// in a vault during a liquidation where the health factor function is decreasing
uint256 internal _dustCollateral;

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

uint256[47] private __gapVaultManager;

/// @inheritdoc IVaultManagerFunctions
function initialize(
Expand Down Expand Up @@ -719,6 +723,7 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
// Because we're rounding up in some divisions, `collateralReleased` can be greater than the `collateralAmount` of the vault
// In this case, `stablecoinAmountToReceive` is still rounded up
if (vault.collateralAmount <= collateralReleased) {
// Liquidators should never get more collateral than what's in the vault
collateralReleased = vault.collateralAmount;
// Remove all the vault's debt (debt repayed + bad debt) from VaultManager totalDebt
totalNormalizedDebt -= vault.normalizedDebt;
Expand Down Expand Up @@ -778,12 +783,11 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
: BASE_PARAMS - liquidationDiscount;
// Same for the surcharge here: it's in fact 1 - the fee taken by the protocol
uint256 surcharge = liquidationSurcharge;
// Checking if we're in a situation where the health factor is an increasing or a decreasing function of the
// amount repaid
uint256 maxAmountToRepay;
uint256 thresholdRepayAmount;
// In the first case, the health factor is an increasing function of the stablecoin amount to repay,
// this means that the liquidator can bring the vault to the target health ratio
// Checking if we're in a situation where the health factor is an increasing or a decreasing function of the
// amount repaid. In the first case, the health factor is an increasing function which means that the liquidator
// can bring the vault to the target health ratio
if (healthFactor * liquidationDiscount * surcharge >= collateralFactor * BASE_PARAMS**2) {
// This is the max amount to repay that will bring the person to the target health factor
// Denom is always positive when a vault gets liquidated in this case and when the health factor
Expand All @@ -794,32 +798,27 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
BASE_PARAMS *
liquidationDiscount) /
(surcharge * targetHealthFactor * liquidationDiscount - (BASE_PARAMS**2) * collateralFactor);
// The quantity below tends to be rounded in the above direction, which means that governance or guardians should
// set the `targetHealthFactor` accordingly
// Need to check for the dust: liquidating should not leave a dusty amount in the vault
if (currentDebt * BASE_PARAMS <= maxAmountToRepay * surcharge + dust * BASE_PARAMS) {
// If liquidating to the target threshold would leave a dusty amount: the liquidator can repay all
// We're rounding up the max amount to repay to make sure all the debt ends up being paid
// and we're computing again the real value of the debt to avoid propagation of rounding errors
// Need to check for the dustas liquidating should not leave a dusty amount in the vault
uint256 dustParameter = dustLiquidation;
if (currentDebt * BASE_PARAMS <= maxAmountToRepay * surcharge + dustParameter * BASE_PARAMS) {
// If liquidating to the target threshold would leave a dusty amount: the liquidator can repay all.
// We're avoiding here propagation of rounding errors and rounding up the max amount to repay to make
// sure all the debt ends up being paid
maxAmountToRepay =
(vault.normalizedDebt * newInterestAccumulator * BASE_PARAMS) /
(surcharge * BASE_INTEREST) +
1;
// In this case the threshold amount is such that it leaves just enough dust: amount is rounded
// down such that if a liquidator repays this amount then there would be more than `dust` left in
// down such that if a liquidator repays this amount then there is more than `dustLiquidation` left in
// the liquidated vault
if (currentDebt > dust)
thresholdRepayAmount = ((currentDebt - dust) * BASE_PARAMS) / surcharge;
// If there is from the beginning a dusty debt (because of an implementation upgrade), then
// liquidator should repay everything that's left
if (currentDebt > dustParameter)
thresholdRepayAmount = ((currentDebt - dustParameter) * BASE_PARAMS) / surcharge;
// If there is from the beginning a dusty debt, then liquidator should repay everything that's left
else thresholdRepayAmount = 1;
}
} else {
// In all cases the liquidator can repay stablecoins such that they'll end up getting exactly the collateral
// In this case, the liquidator can repay stablecoins such that they'll end up getting exactly the collateral
// in the liquidated vault
// Rounding up to make sure all gets liquidated in this case: the liquidator will never get more than the collateral
// amount in the vault however: we're performing the computation of the `collateralAmountInStable` again to avoid
// propagation of rounding errors
maxAmountToRepay =
(vault.collateralAmount * liquidationDiscount * oracleValue) /
(BASE_PARAMS * _collatBase) +
Expand Down Expand Up @@ -864,7 +863,7 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {
borrowFee = param;
} else if (what == "RF") {
// As liquidation surcharge is stored as `1-fee` and as we need `repayFee` to be smaller
// then the liquidation surcharge, then we need to have:
// than the liquidation surcharge, we need to have:
// `liquidationSurcharge <= BASE_PARAMS - repayFee` and as such `liquidationSurcharge + repayFee <= BASE_PARAMS`
if (param + liquidationSurcharge > BASE_PARAMS) revert TooHighParameterValue();
repayFee = param;
Expand Down Expand Up @@ -933,10 +932,17 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions {

/// @notice Sets the dust variables
/// @param _dust New minimum debt allowed
/// @param _dustLiquidation New `dustLiquidation` value
/// @param dustCollateral_ New minimum collateral allowed in a vault after a liquidation
/// @dev dustCollateral_ is in stable value
function setDusts(uint256 _dust, uint256 dustCollateral_) external onlyGovernor {
function setDusts(
uint256 _dust,
uint256 _dustLiquidation,
uint256 dustCollateral_
) external onlyGovernor {
if (_dust > _dustLiquidation) revert InvalidParameterValue();
dust = _dust;
dustLiquidation = _dustLiquidation;
_dustCollateral = dustCollateral_;
}

Expand Down
4 changes: 2 additions & 2 deletions deploy/5_vaultManagerImplementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ const func: DeployFunction = async ({ deployments, ethers }) => {
const { deployer } = await ethers.getNamedSigners();

console.log('Now deploying the implementation for VaultManager');
await deploy('VaultManager_Implementation', {
await deploy('VaultManager_V2_0_Implementation', {
contract: 'VaultManagerLiquidationBoost',
from: deployer.address,
args: [],
log: !argv.ci,
});

const vaultManagerImplementation = (await ethers.getContract('VaultManager_Implementation')).address;
const vaultManagerImplementation = (await ethers.getContract('VaultManager_V2_0_Implementation')).address;

console.log(`Successfully deployed the implementation for VaultManager at ${vaultManagerImplementation}`);
console.log('');
Expand Down
9 changes: 5 additions & 4 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,10 @@ const config: HardhatUserConfig = {
forking: {
enabled: argv.fork || false,
// Mainnet
/*

url: nodeUrl('fork'),
blockNumber: 15975107,
*/
blockNumber: 16218353,

// Polygon
/*
url: nodeUrl('forkpolygon'),
Expand All @@ -164,9 +164,10 @@ const config: HardhatUserConfig = {
blockNumber: 19356874,
*/
// Avalanche

/*
url: nodeUrl('avalanche'),
blockNumber: 23545788,
*/
},
mining: argv.disableAutoMining
? {
Expand Down
7 changes: 5 additions & 2 deletions scripts/mainnet-fork/upgradeVaultManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ async function main() {

const vaultManagerAddress = '0x73aaf8694BA137a7537E7EF544fcf5E2475f227B';

const implementation = (await deployments.get('VaultManagerNoDust_Implementation')).address;
const implementation = (await deployments.get('VaultManager_V2_0_Implementation')).address;

console.log(implementation);

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

Expand Down Expand Up @@ -58,8 +60,9 @@ async function main() {
console.log(await vaultManager.name());
console.log(await vaultManager.symbol());

await vaultManager.connect(signer).setDusts(10, 10);
await vaultManager.connect(signer).setDusts(10, 10, 10);
console.log((await vaultManager.dust()).toString());
console.log((await vaultManager.dustLiquidation()).toString());

console.log((await vaultManager.vaultData(8)).collateralAmount.toString());
console.log((await vaultManager.vaultData(8)).normalizedDebt.toString());
Expand Down
2 changes: 1 addition & 1 deletion test/hardhat/reactor/reactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ contract('Reactor', () => {
stableMaster = await new MockStableMaster__factory(deployer).deploy();

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

reactor = (await deployUpgradeable(new Reactor__factory(deployer))) as Reactor;
Expand Down
8 changes: 4 additions & 4 deletions test/hardhat/vaultManager/vaultManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ contract('VaultManagerLiquidationBoost', () => {
await vaultManager.initialize(treasury.address, collateral.address, oracle.address, params, 'USDC/agEUR');
await vaultManager.connect(guardian).togglePause();
await vaultManager.connect(governor).setUint64(params.borrowFee, formatBytes32String('BF'));
await vaultManager.connect(governor).setDusts(0.1e9, 0.1e9);
await vaultManager.connect(governor).setDusts(0.1e9, 0.1e9, 0.1e9);
});
describe('oracle', () => {
it('success - read', async () => {
Expand Down Expand Up @@ -1178,7 +1178,7 @@ contract('VaultManagerLiquidationBoost', () => {
await treasury.addMinter(agToken.address, vaultManager.address);
oracle = await new MockOracle__factory(deployer).deploy(parseUnits('2', 18), treasury.address);
await vaultManager.initialize(treasury.address, agToken.address, oracle.address, params, 'USDC/agEUR');
await vaultManager.connect(governor).setDusts(0.1e9, 0.1e9);
await vaultManager.connect(governor).setDusts(0.1e9, 0.1e9, 0.1e9);
await vaultManager.connect(guardian).togglePause();
});
it('success - allowance given', async () => {
Expand Down Expand Up @@ -2263,7 +2263,7 @@ contract('VaultManagerLiquidationBoost', () => {
params.interestRate = parseEther('0');
params.borrowFee = 0;
await vaultManager.initialize(treasury.address, collateral.address, oracle.address, params, 'USDC/agEUR');
await vaultManager.connect(governor).setDusts(parseEther('0.5'), parseEther('0.5'));
await vaultManager.connect(governor).setDusts(parseEther('0.5'), parseEther('0.5'), parseEther('0.5'));
await vaultManager.connect(guardian).togglePause();
await treasury.setVaultManager2(vaultManager.address);
await treasury.addMinter(agToken.address, vaultManager.address);
Expand All @@ -2289,7 +2289,7 @@ contract('VaultManagerLiquidationBoost', () => {
params.interestRate = parseEther('0');
params.borrowFee = 0;
await vaultManager.initialize(treasury.address, collateral.address, oracle.address, params, 'USDC/agEUR');
await vaultManager.connect(governor).setDusts(parseEther('0.5'), parseEther('0.5'));
await vaultManager.connect(governor).setDusts(parseEther('0.5'), parseEther('0.5'), parseEther('0.5'));
await vaultManager.connect(guardian).togglePause();
await treasury.setVaultManager2(vaultManager.address);
await treasury.addMinter(agToken.address, vaultManager.address);
Expand Down
Loading

0 comments on commit 6240aaa

Please sign in to comment.