Skip to content
Open
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d2f5cd0
feat: add new vars in AccountLiquiditySnapshot
Debugger022 Jul 7, 2025
1f2d92b
feat: modify comptroller interface
Debugger022 Jul 7, 2025
a2ac784
feat: add dynamic close factor and liquidation incentive
Debugger022 Jul 7, 2025
39c970d
feat: add contract-sizer in hardhat config
Debugger022 Jul 8, 2025
5aac9d2
chore: update yarn.lock
Debugger022 Jul 8, 2025
083a1f7
feat: move liquidation logic to Liquidation library
Debugger022 Jul 8, 2025
8c964fa
feat: update comptroller to use Liquidation library functions
Debugger022 Jul 8, 2025
7c65f24
feat: add maximum liquidation incentive per asset
Debugger022 Jul 9, 2025
32bf187
feat: update liquidation library
Debugger022 Jul 9, 2025
2716fec
refactor: remove pool liquidation Incentive reference
Debugger022 Jul 9, 2025
e1f58da
feat: moved reward updates logic to Rewards library
Debugger022 Jul 10, 2025
0921fce
refactor: removed liquidation Incentive mapping
Debugger022 Jul 10, 2025
dec2414
refactor: reduced comptroller size
Debugger022 Jul 10, 2025
f2ef6fd
fix: minor fix
Debugger022 Jul 10, 2025
27cf44c
fix: adjust heal account tests for dynamic factors
Debugger022 Jul 10, 2025
47ba38e
fix: adjust liquidate account tests for dynamic factors
Debugger022 Jul 14, 2025
876e44e
refactor: add zero checks in snapshot calculations
Debugger022 Jul 14, 2025
5860efe
test: fix hooks and setters test
Debugger022 Jul 14, 2025
fc9c6a7
test: fix seize tokens test
Debugger022 Jul 14, 2025
691c424
refactor: moved rewards logic to internal functions
Debugger022 Jul 15, 2025
bebf45d
test: fix pool lens test
Debugger022 Jul 15, 2025
2811258
test: fix tests
Debugger022 Jul 15, 2025
8876c29
feat: getter for liquidation incentive per market
Debugger022 Jul 15, 2025
e6e906f
fix: fixed vTokens test
Debugger022 Jul 15, 2025
749dbb6
test: fix NativeToken gateway test
Debugger022 Jul 15, 2025
e8ea512
fix: Average liquidation incentive calculation
Debugger022 Jul 16, 2025
c4a5768
refactor: corrected addPool signature
Debugger022 Jul 16, 2025
fe24745
refactor: adjust computation in calculateIncentiveAdjustedDebt
Debugger022 Jul 16, 2025
bfd294f
test: fixed integration tests
Debugger022 Jul 16, 2025
232421a
fix: fix Pool lens test
Debugger022 Jul 17, 2025
fbde27d
fix: corrected averageLT calculation
Debugger022 Jul 17, 2025
438695d
feat: add Toxic liquidation check
Debugger022 Jul 17, 2025
58e2678
feat: update comptroller interface
Debugger022 Jul 17, 2025
2413962
test: refactored integration tests
Debugger022 Jul 17, 2025
c5564d9
fix: fixed references for averageLT
Debugger022 Jul 17, 2025
525f1cf
fix: fixed storage layout
Debugger022 Jul 22, 2025
c825a8e
feat: external Liquidaiton Manager contract instead of library
Debugger022 Jul 22, 2025
c907123
feat: Liquidation Manager interface
Debugger022 Jul 22, 2025
b6f0e5e
feat: update comptroller interface
Debugger022 Jul 22, 2025
d072b0a
feat: add liquidation manager setter and refactor dependencies
Debugger022 Jul 22, 2025
d86ccaa
refactor: add MarketListed internal function to reduce comptroller size
Debugger022 Jul 22, 2025
ebcd3e4
feat: add natspec comments for reward functions
Debugger022 Jul 22, 2025
8451373
fix: fix tests
Debugger022 Jul 23, 2025
d8bc0ce
refactor: move order processing back to comptroller
Debugger022 Jul 23, 2025
dbf5739
fix: integration test
Debugger022 Jul 23, 2025
3177bf8
test: fixed fork tests
Debugger022 Jul 24, 2025
ec585af
feat: liquidation Manager for common functionalities of core and IL
Debugger022 Jul 28, 2025
1dc088c
feat: IL specific liquidation manager
Debugger022 Jul 28, 2025
91c22c3
refactor: using ILLiquidation manager in comptroller
Debugger022 Jul 28, 2025
b7ad5ae
refactor: moved some logic to Liquidation Manager
Debugger022 Jul 28, 2025
e7285d6
test: refactor tests
Debugger022 Jul 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
310 changes: 170 additions & 140 deletions contracts/Comptroller.sol

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions contracts/ComptrollerInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ interface ComptrollerInterface {
/*** Liquidity/Liquidation Calculations ***/

function liquidateCalculateSeizeTokens(
address borrower,
address vTokenBorrowed,
address vTokenCollateral,
uint256 repayAmount
Expand All @@ -103,6 +104,10 @@ interface ComptrollerInterface {
function getAllMarkets() external view returns (VToken[] memory);

function actionPaused(address market, Action action) external view returns (bool);

function getDynamicLiquidationIncentive(address borrower, address market) external view returns (uint256);

function getMarketLiquidationIncentive(address vToken) external view returns (uint256);
}

/**
Expand Down Expand Up @@ -132,4 +137,8 @@ interface ComptrollerViewInterface {
function supplyCaps(address) external view returns (uint256);

function approvedDelegates(address user, address delegate) external view returns (bool);

function getDynamicLiquidationIncentive(address borrower, address market) external view returns (uint256);

function getMarketLiquidationIncentive(address vToken) external view returns (uint256);
}
11 changes: 6 additions & 5 deletions contracts/ComptrollerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ contract ComptrollerStorage {
uint256 effects;
uint256 liquidity;
uint256 shortfall;
uint256 averageLT; // Average liquidation threshold of all assets in the snapshot
uint256 healthFactor; // Health factor of the account, calculated as (weightedCollateral / borrows)
uint256 healthFactorThreshold; // Health factor threshold for liquidation, calculated as (averageLT * (1e18 + LiquidationIncentiveAvg) / 1e18)
uint256 liquidationIncentiveAvg; // Average liquidation incentive of all assets in the snapshot
}

struct RewardSpeeds {
Expand All @@ -48,6 +52,8 @@ contract ComptrollerStorage {
uint256 liquidationThresholdMantissa;
// Per-market mapping of "accounts in this asset"
mapping(address => bool) accountMembership;
// discount on collateral that a liquidator receives when liquidating a borrow in this market
uint256 liquidationIncentiveMantissa;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint256 liquidationIncentiveMantissa;
uint256 maxLiquidationIncentiveMantissa;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

/**
Expand All @@ -60,11 +66,6 @@ contract ComptrollerStorage {
*/
uint256 public closeFactorMantissa;

/**
* @notice Multiplier representing the discount on collateral that a liquidator receives
*/
uint256 public liquidationIncentiveMantissa;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintain the attribute, to not break the storage layout. Simply rename it to "deprecatedLiquidationIncentiveMantissa"?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/**
* @notice Per-account mapping of "assets you are in"
*/
Expand Down
8 changes: 3 additions & 5 deletions contracts/Lens/PoolLens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { TimeManagerV8 } from "@venusprotocol/solidity-utilities/contracts/TimeM
* for all pools within the lending protocol can be acquired through the function `getAllPools()`. Additionally, the following records can be
* looked up for specific pools and markets:
- the vToken balance of a given user;
- the pool data (oracle address, associated vToken, liquidation incentive, etc) of a pool via its associated comptroller address;
- the pool data (oracle address, associated vToken etc) of a pool via its associated comptroller address;
- the vToken address in a pool for a given asset;
- a list of all pools that support an asset;
- the underlying asset price of a vToken;
Expand All @@ -41,7 +41,6 @@ contract PoolLens is ExponentialNoError, TimeManagerV8 {
string description;
address priceOracle;
uint256 closeFactor;
uint256 liquidationIncentive;
uint256 minLiquidatableCollateral;
VTokenMetadata[] vTokens;
}
Expand Down Expand Up @@ -270,7 +269,7 @@ contract PoolLens is ExponentialNoError, TimeManagerV8 {
*
* @param comptrollerAddress Address of the comptroller
*
* @return badDebtSummary A struct with comptroller address, total bad debut denominated in usd, and
* @return badDebtSummary A struct with comptroller address, total bad debt denominated in usd, and
* a break down of bad debt by market
*/
function getPoolBadDebt(address comptrollerAddress) external view returns (BadDebtSummary memory) {
Expand All @@ -287,7 +286,7 @@ contract PoolLens is ExponentialNoError, TimeManagerV8 {
badDebtSummary.comptroller = comptrollerAddress;
badDebtSummary.badDebts = badDebts;

// // Calculate the bad debt is USD per market
// Calculate the bad debt in USD per market
for (uint256 i; i < markets.length; ++i) {
BadDebt memory badDebt;
badDebt.vTokenAddress = address(markets[i]);
Expand Down Expand Up @@ -368,7 +367,6 @@ contract PoolLens is ExponentialNoError, TimeManagerV8 {
vTokens: vTokenMetadataItems,
priceOracle: address(comptrollerViewInstance.oracle()),
closeFactor: comptrollerViewInstance.closeFactorMantissa(),
liquidationIncentive: comptrollerViewInstance.liquidationIncentiveMantissa(),
minLiquidatableCollateral: comptrollerViewInstance.minLiquidatableCollateral()
});

Expand Down
5 changes: 1 addition & 4 deletions contracts/Pool/PoolRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ contract PoolRegistry is Ownable2StepUpgradeable, AccessControlledV8, PoolRegist
* @param name The name of the pool
* @param comptroller Pool's Comptroller contract
* @param closeFactor The pool's close factor (scaled by 1e18)
* @param liquidationIncentive The pool's liquidation incentive (scaled by 1e18)
* @param minLiquidatableCollateral Minimal collateral for regular (non-batch) liquidations flow
* @return index The index of the registered Venus pool
* @custom:error ZeroAddressNotAllowed is thrown when Comptroller address is zero
Expand All @@ -134,10 +133,9 @@ contract PoolRegistry is Ownable2StepUpgradeable, AccessControlledV8, PoolRegist
string calldata name,
Comptroller comptroller,
uint256 closeFactor,
uint256 liquidationIncentive,
uint256 minLiquidatableCollateral
) external virtual returns (uint256 index) {
_checkAccessAllowed("addPool(string,address,uint256,uint256,uint256)");
_checkAccessAllowed("addPool(string,address,uint256,uint256)");
// Input validation
ensureNonzeroAddress(address(comptroller));
ensureNonzeroAddress(address(comptroller.oracle()));
Expand All @@ -146,7 +144,6 @@ contract PoolRegistry is Ownable2StepUpgradeable, AccessControlledV8, PoolRegist

// Set Venus pool parameters
comptroller.setCloseFactor(closeFactor);
comptroller.setLiquidationIncentive(liquidationIncentive);
comptroller.setMinLiquidatableCollateral(minLiquidatableCollateral);

return poolId;
Expand Down
9 changes: 7 additions & 2 deletions contracts/VToken.sol

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would avoid changes in the VToken contract, if it's doable. Because vBNB cannot be upgraded

Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,10 @@ contract VToken is
*/
function setProtocolSeizeShare(uint256 newProtocolSeizeShareMantissa_) external {
_checkAccessAllowed("setProtocolSeizeShare(uint256)");
uint256 liquidationIncentive = ComptrollerViewInterface(address(comptroller)).liquidationIncentiveMantissa();

uint256 liquidationIncentive = ComptrollerViewInterface(address(comptroller)).getMarketLiquidationIncentive(
address(this)
);
if (newProtocolSeizeShareMantissa_ + MANTISSA_ONE > liquidationIncentive) {
revert ProtocolSeizeShareTooBig();
}
Expand Down Expand Up @@ -1221,6 +1224,7 @@ contract VToken is

/* We calculate the number of collateral tokens that will be seized */
(uint256 amountSeizeError, uint256 seizeTokens) = comptroller.liquidateCalculateSeizeTokens(
borrower,
address(this),
address(vTokenCollateral),
actualRepayAmount
Expand Down Expand Up @@ -1274,8 +1278,9 @@ contract VToken is
* borrowerTokensNew = accountTokens[borrower] - seizeTokens
* liquidatorTokensNew = accountTokens[liquidator] + seizeTokens
*/

uint256 liquidationIncentiveMantissa = ComptrollerViewInterface(address(comptroller))
.liquidationIncentiveMantissa();
.getDynamicLiquidationIncentive(borrower, address(this));
uint256 numerator = mul_(seizeTokens, Exp({ mantissa: protocolSeizeShareMantissa }));
uint256 protocolSeizeTokens = div_(numerator, Exp({ mantissa: liquidationIncentiveMantissa }));
uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;
Expand Down
2 changes: 1 addition & 1 deletion contracts/WUSDMLiquidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ contract WUSDMLiquidator is Ownable2StepUpgradeable {
}

function _configureMarkets() internal {
(, uint256 wUSDMCollateralFactor, uint256 wUSDMLiquidationThreshold) = COMPTROLLER.markets(address(VWUSDM));
(, uint256 wUSDMCollateralFactor, uint256 wUSDMLiquidationThreshold, ) = COMPTROLLER.markets(address(VWUSDM));
_originalConfig = OriginalConfig({
minLiquidatableCollateral: COMPTROLLER.minLiquidatableCollateral(),
closeFactor: COMPTROLLER.closeFactorMantissa(),
Expand Down
131 changes: 131 additions & 0 deletions contracts/lib/ExponentialNoError.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.25;

import { EXP_SCALE as EXP_SCALE_, MANTISSA_ONE as MANTISSA_ONE_ } from "./constants.sol";

library ExponentialNoError {
struct Exp {
uint256 mantissa;
}

struct Double {
uint256 mantissa;
}

uint256 internal constant EXP_SCALE = EXP_SCALE_;
uint256 internal constant DOUBLE_SCALE = 1e36;
uint256 internal constant HALF_EXP_SCALE = EXP_SCALE / 2;
uint256 internal constant MANTISSA_ONE = MANTISSA_ONE_;

function truncate(Exp memory exp) internal pure returns (uint256) {
return exp.mantissa / EXP_SCALE;
}

function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) {
Exp memory product = mul_(a, scalar);
return truncate(product);
}

function mul_ScalarTruncateAddUInt(Exp memory a, uint256 scalar, uint256 addend) internal pure returns (uint256) {
Exp memory product = mul_(a, scalar);
return add_(truncate(product), addend);
}

function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {
return left.mantissa < right.mantissa;
}

function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) {
require(n <= type(uint224).max, errorMessage);
return uint224(n);
}

function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {
require(n <= type(uint32).max, errorMessage);
return uint32(n);
}

function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
return Exp(add_(a.mantissa, b.mantissa));
}

function add_(Double memory a, Double memory b) internal pure returns (Double memory) {
return Double(add_(a.mantissa, b.mantissa));
}

function add_(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}

function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
return Exp(sub_(a.mantissa, b.mantissa));
}

function sub_(Double memory a, Double memory b) internal pure returns (Double memory) {
return Double(sub_(a.mantissa, b.mantissa));
}

function sub_(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}

function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
return Exp(mul_(a.mantissa, b.mantissa) / EXP_SCALE);
}

function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {
return Exp(mul_(a.mantissa, b));
}

function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {
return mul_(a, b.mantissa) / EXP_SCALE;
}

function mul_(Double memory a, Double memory b) internal pure returns (Double memory) {
return Double(mul_(a.mantissa, b.mantissa) / DOUBLE_SCALE);
}

function mul_(Double memory a, uint256 b) internal pure returns (Double memory) {
return Double(mul_(a.mantissa, b));
}

function mul_(uint256 a, Double memory b) internal pure returns (uint256) {
return mul_(a, b.mantissa) / DOUBLE_SCALE;
}

function mul_(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}

function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
return Exp(div_(mul_(a.mantissa, EXP_SCALE), b.mantissa));
}

function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {
return Exp(div_(a.mantissa, b));
}

function div_(uint256 a, Exp memory b) internal pure returns (uint256) {
return div_(mul_(a, EXP_SCALE), b.mantissa);
}

function div_(Double memory a, Double memory b) internal pure returns (Double memory) {
return Double(div_(mul_(a.mantissa, DOUBLE_SCALE), b.mantissa));
}

function div_(Double memory a, uint256 b) internal pure returns (Double memory) {
return Double(div_(a.mantissa, b));
}

function div_(uint256 a, Double memory b) internal pure returns (uint256) {
return div_(mul_(a, DOUBLE_SCALE), b.mantissa);
}

function div_(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}

function fraction(uint256 a, uint256 b) internal pure returns (Double memory) {
return Double(div_(mul_(a, DOUBLE_SCALE), b));
}
}
Loading