Skip to content

Commit

Permalink
🐝 Add liquidators whitelist
Browse files Browse the repository at this point in the history
  • Loading branch information
nezouse authored and MiksuJak committed Apr 29, 2024
1 parent 08a74a1 commit f5e008a
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 1 deletion.
11 changes: 11 additions & 0 deletions contracts/AdminContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ contract AdminContract is IAdminContract, UUPSUpgradeable, OwnableUpgradeable, A

mapping(address => mapping(address => bool)) internal collateralWhitelistedAddresses;

mapping(address => bool) internal whitelistedLiquidators;

// list of all collateral types in collateralParams (active and deprecated)
// Addresses for easy access
address[] public validCollateral; // index maps to token address.
Expand Down Expand Up @@ -257,6 +259,11 @@ contract AdminContract is IAdminContract, UUPSUpgradeable, OwnableUpgradeable, A
emit AddressCollateralWhitelisted(_collateral, _address, _whitelisted);
}

function setLiquidatorWhitelisted(address _liquidator, bool _whitelisted) external onlyTimelock {
whitelistedLiquidators[_liquidator] = _whitelisted;
emit LiquidatorWhitelisted(_liquidator, _whitelisted);
}

function setRedemptionBaseFeeEnabled(address _collateral, bool _enabled) external onlyTimelock {
collateralParams[_collateral].redemptionBaseFeeEnabled = _enabled;
emit BaseFeeEnabledChanged(_collateral, _enabled);
Expand Down Expand Up @@ -333,6 +340,10 @@ contract AdminContract is IAdminContract, UUPSUpgradeable, OwnableUpgradeable, A
return collateralWhitelistedAddresses[_collateral][_address];
}

function getIsLiquidatorWhitelisted(address _liquidator) external view returns (bool) {
return whitelistedLiquidators[_liquidator];
}

function getRedemptionBaseFeeEnabled(address _collateral) external view override returns (bool) {
return collateralParams[_collateral].redemptionBaseFeeEnabled;
}
Expand Down
5 changes: 5 additions & 0 deletions contracts/Interfaces/IAdminContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ interface IAdminContract {
event RedemptionBlockTimestampChanged(address _collateral, uint256 _blockTimestamp);
event AddressCollateralWhitelisted(address _collateral, address _address, bool _whitelisted);
event BaseFeeEnabledChanged(address _collateral, bool _enabled);
event LiquidatorWhitelisted(address _liquidator, bool _whitelisted);

// Functions --------------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -83,6 +84,8 @@ interface IAdminContract {

function setAddressCollateralWhitelisted(address _collateral, address _address, bool _whitelisted) external;

function setLiquidatorWhitelisted(address _liquidator, bool _whitelisted) external;

function setRedemptionBaseFeeEnabled(address _collateral, bool _enabled) external;

function getIndex(address _collateral) external view returns (uint256);
Expand Down Expand Up @@ -111,5 +114,7 @@ interface IAdminContract {

function getIsAddressCollateralWhitelisted(address _collateral, address _address) external view returns (bool);

function getIsLiquidatorWhitelisted(address _liquidator) external view returns (bool);

function getRedemptionBaseFeeEnabled(address _collateral) external view returns (bool);
}
1 change: 1 addition & 0 deletions contracts/Interfaces/IVesselManagerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ interface IVesselManagerOperations is ITrinityBase {
error VesselManagerOperations__InvalidParam();
error VesselManagerOperations__NotTimelock();
error VesselManagerOperations__AddressNotCollateralWhitelisted();
error VesselManagerOperations__LiquidatorNotWhitelisted();

// Structs ----------------------------------------------------------------------------------------------------------

Expand Down
9 changes: 9 additions & 0 deletions contracts/VesselManagerOperations.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ contract VesselManagerOperations is IVesselManagerOperations, UUPSUpgradeable, R
* starting from the one with the lowest collateral ratio in the system, and moving upwards.
*/
function liquidateVessels(address _asset, uint256 _n) external override nonReentrant {
address liquidator = msg.sender;
if (!IAdminContract(adminContract).getIsLiquidatorWhitelisted(liquidator)) {
revert VesselManagerOperations__LiquidatorNotWhitelisted();
}

LocalVariables_OuterLiquidationFunction memory vars;
LiquidationTotals memory totals;
vars.price = IPriceFeed(priceFeed).fetchPrice(_asset);
Expand Down Expand Up @@ -102,6 +107,10 @@ contract VesselManagerOperations is IVesselManagerOperations, UUPSUpgradeable, R
* Attempt to liquidate a custom list of vessels provided by the caller.
*/
function batchLiquidateVessels(address _asset, address[] memory _vesselArray) public override nonReentrant {
address liquidator = msg.sender;
if (!IAdminContract(adminContract).getIsLiquidatorWhitelisted(liquidator)) {
revert VesselManagerOperations__LiquidatorNotWhitelisted();
}
if (_vesselArray.length == 0 || _vesselArray.length > BATCH_SIZE_LIMIT) {
revert VesselManagerOperations__InvalidArraySize();
}
Expand Down
12 changes: 12 additions & 0 deletions test/trinity/AdminContractTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,18 @@ contract("AdminContract", async accounts => {
await assertRevert(adminContract.setAddressCollateralWhitelisted(erc20.address, ZERO_ADDRESS, false, {from: user}))
})

it('setLiquidatorWhitelisted: Owner change parameter - Valid Owner', async () => {
await adminContract.setLiquidatorWhitelisted(ZERO_ADDRESS, true)
assert.isTrue(await adminContract.getIsLiquidatorWhitelisted(ZERO_ADDRESS))
await adminContract.setLiquidatorWhitelisted(ZERO_ADDRESS, false)
assert.isFalse(await adminContract.getIsLiquidatorWhitelisted(ZERO_ADDRESS))
})

it('setLiquidatorWhitelisted: Owner change parameter - Invalid Owner', async () => {
await assertRevert(adminContract.setLiquidatorWhitelisted(ZERO_ADDRESS, true, {from: user}))
await assertRevert(adminContract.setLiquidatorWhitelisted(ZERO_ADDRESS, false, {from: user}))
})

it('setRedemptionBaseFeeEnabled: Owner change parameter - Valid Owner', async () => {
await adminContract.setRedemptionBaseFeeEnabled(ZERO_ADDRESS, true)
assert.isTrue(await adminContract.getRedemptionBaseFeeEnabled(ZERO_ADDRESS))
Expand Down
6 changes: 5 additions & 1 deletion test/trinity/BorrowerOperationsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ const deploy = async (treasury, distributor, mintingAccounts) => {
vesselManagerOperations = contracts.core.vesselManagerOperations
shortTimelock = contracts.core.shortTimelock
longTimelock = contracts.core.longTimelock

for(const account of mintingAccounts) {
await adminContract.setLiquidatorWhitelisted(account, true)
}
}

contract("BorrowerOperations", async accounts => {
Expand All @@ -59,7 +63,7 @@ contract("BorrowerOperations", async accounts => {

describe("BorrowerOperations Mechanisms", async () => {
before(async () => {
await deploy(treasury, distributor, [])
await deploy(treasury, distributor, accounts.slice(0, 20))

MIN_NET_DEBT_ERC20 = await adminContract.getMinNetDebt(erc20.address)
BORROWING_FEE_ERC20 = await adminContract.getBorrowingFee(erc20.address)
Expand Down
4 changes: 4 additions & 0 deletions test/trinity/CollSurplusPoolTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ const deploy = async (treasury, distributor, mintingAccounts) => {
vesselManagerOperations = contracts.core.vesselManagerOperations
shortTimelock = contracts.core.shortTimelock
longTimelock = contracts.core.longTimelock

for(const account of mintingAccounts) {
await adminContract.setLiquidatorWhitelisted(account, true)
}
}

contract("CollSurplusPool", async accounts => {
Expand Down
4 changes: 4 additions & 0 deletions test/trinity/GasCompensationTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ const deploy = async (treasury, distributor, mintingAccounts) => {
longTimelock = contracts.core.longTimelock

validCollateral = await adminContract.getValidCollateral()

for(const account of mintingAccounts) {
await adminContract.setLiquidatorWhitelisted(account, true)
}
}

contract("Gas compensation tests", async accounts => {
Expand Down
4 changes: 4 additions & 0 deletions test/trinity/StabilityPoolTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ const deploy = async (treasury, distributor, mintingAccounts) => {

// getDepositorGains() expects a sorted collateral array
validCollateral = validCollateral.slice(0).sort((a, b) => toBN(a.toLowerCase()).sub(toBN(b.toLowerCase())))

for(const account of mintingAccounts) {
await adminContract.setLiquidatorWhitelisted(account, true)
}
}

contract("StabilityPool", async accounts => {
Expand Down
42 changes: 42 additions & 0 deletions test/trinity/VesselManagerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const deploy = async (treasury, distributor, mintingAccounts) => {

for(const account of mintingAccounts) {
await adminContract.setAddressCollateralWhitelisted(erc20.address, account, true)
await adminContract.setLiquidatorWhitelisted(account, true)
}
}

Expand Down Expand Up @@ -131,6 +132,24 @@ contract("VesselManager", async accounts => {
})

describe("Liquidations", async () => {
it("liquidate(): reverts when not whitelisted", async () => {
await openVessel({
asset: erc20.address,
ICR: toBN(dec(4, 18)),
extraParams: { from: alice },
})

await adminContract.setLiquidatorWhitelisted(alice, false)

try {
const tx = await vesselManagerOperations.liquidate(erc20.address, alice, {from: alice})
assert.isFalse(tx.receipt.status)
} catch (err) {
assert.include(err.message, "revert")
assert.include(err.message, "VesselManagerOperations__LiquidatorNotWhitelisted()")
}
})

it("liquidate(): closes a Vessel that has ICR < MCR", async () => {
await openVessel({
asset: erc20.address,
Expand Down Expand Up @@ -1399,6 +1418,17 @@ contract("VesselManager", async accounts => {
})

// --- liquidateVessels() ---
it("liquidateVessels(): reverts when not whitelisted", async () => {
await adminContract.setLiquidatorWhitelisted(alice, false)

try {
const tx = await vesselManagerOperations.liquidateVessels(erc20.address, 2, {from: alice})
assert.isFalse(tx.receipt.status)
} catch (err) {
assert.include(err.message, "revert")
assert.include(err.message, "VesselManagerOperations__LiquidatorNotWhitelisted()")
}
})

it("liquidateVessels(): liquidates a Vessel that a) was skipped in a previous liquidation and b) has pending rewards", async () => {
// A, B, C, D, E open vessels
Expand Down Expand Up @@ -2430,6 +2460,18 @@ contract("VesselManager", async accounts => {
// --- batchLiquidateVessels() ---

describe("Batch Liquidations", async () => {
it("batchLiquidateVessels(): reverts when not whitelisted", async () => {
await adminContract.setLiquidatorWhitelisted(alice, false)

try {
const tx = await vesselManagerOperations.batchLiquidateVessels(erc20.address, [], {from: alice})
assert.isFalse(tx.receipt.status)
} catch (err) {
assert.include(err.message, "revert")
assert.include(err.message, "VesselManagerOperations__LiquidatorNotWhitelisted()")
}
})

it("batchLiquidateVessels(): liquidates a Vessel that a) was skipped in a previous liquidation and b) has pending rewards", async () => {
// A, B, C, D, E open vessels

Expand Down
4 changes: 4 additions & 0 deletions test/trinity/VesselManager_LiquidationRewardsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ const deploy = async (treasury, distributor, mintingAccounts) => {
vesselManagerOperations = contracts.core.vesselManagerOperations
shortTimelock = contracts.core.shortTimelock
longTimelock = contracts.core.longTimelock

for(const account of mintingAccounts) {
await adminContract.setLiquidatorWhitelisted(account, true)
}
}

contract("VesselManager - Redistribution reward calculations", async accounts => {
Expand Down
1 change: 1 addition & 0 deletions test/trinity/VesselManager_RecoveryModeTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const deploy = async (treasury, distributor, mintingAccounts) => {

for(const account of mintingAccounts) {
await adminContract.setAddressCollateralWhitelisted(erc20.address, account, true)
await adminContract.setLiquidatorWhitelisted(account, true)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ const deploy = async (treasury, distributor, mintingAccounts) => {

// getDepositorGains() expects a sorted collateral array
validCollateral = validCollateral.slice(0).sort((a, b) => toBN(a.toLowerCase()).sub(toBN(b.toLowerCase())))

for(const account of mintingAccounts) {
await adminContract.setLiquidatorWhitelisted(account, true)
}
}

contract("VesselManager - in Recovery Mode - back to normal mode in 1 tx", async accounts => {
Expand Down

0 comments on commit f5e008a

Please sign in to comment.