Skip to content

Commit

Permalink
Merge pull request #438 from VenusProtocol/feat/borrow-cap-fix
Browse files Browse the repository at this point in the history
[VEN-2346] [VEN-2322] Fix Borrow Cap 0 Logic and Unlist Market
  • Loading branch information
web3rover authored Sep 5, 2024
2 parents b655cce + f351525 commit 8c821ac
Show file tree
Hide file tree
Showing 25 changed files with 1,804 additions and 722 deletions.
Binary file added audits/099_unlistMarkets_certik_20240409.pdf
Binary file not shown.
Binary file added audits/102_unlistMarkets_fairyproof_20240328.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion contracts/Comptroller/ComptrollerStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ contract ComptrollerV3Storage is ComptrollerV2Storage {
/// @notice The borrowCapGuardian can set borrowCaps to any number for any market. Lowering the borrow cap could disable borrowing on the given market.
address public borrowCapGuardian;

/// @notice Borrow caps enforced by borrowAllowed for each vToken address. Defaults to zero which corresponds to unlimited borrowing.
/// @notice Borrow caps enforced by borrowAllowed for each vToken address.
mapping(address => uint256) public borrowCaps;
}

Expand Down
59 changes: 58 additions & 1 deletion contracts/Comptroller/Diamond/facets/MarketFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ contract MarketFacet is IMarketFacet, FacetBase {
/// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account
event DelegateUpdated(address indexed approver, address indexed delegate, bool approved);

/// @notice Emitted when an admin unlists a market
event MarketUnlisted(address indexed vToken);

/// @notice Indicator that this is a Comptroller contract (for inspection)
function isComptroller() public pure returns (bool) {
return true;
Expand All @@ -33,7 +36,25 @@ contract MarketFacet is IMarketFacet, FacetBase {
* @return A dynamic list with the assets the account has entered
*/
function getAssetsIn(address account) external view returns (VToken[] memory) {
return accountAssets[account];
uint256 len;
VToken[] memory _accountAssets = accountAssets[account];
uint256 _accountAssetsLength = _accountAssets.length;

VToken[] memory assetsIn = new VToken[](_accountAssetsLength);

for (uint256 i; i < _accountAssetsLength; ++i) {
Market memory market = markets[address(_accountAssets[i])];
if (market.isListed) {
assetsIn[len] = _accountAssets[i];
++len;
}
}

assembly {
mstore(assetsIn, len)
}

return assetsIn;
}

/**
Expand Down Expand Up @@ -112,6 +133,42 @@ contract MarketFacet is IMarketFacet, FacetBase {
return results;
}

/**
* @notice Unlist a market by setting isListed to false
* @dev Checks if market actions are paused and borrowCap/supplyCap/CF are set to 0
* @param market The address of the market (vToken) to unlist
* @return uint256 0=success, otherwise a failure. (See enum Error for details)
*/
function unlistMarket(address market) external returns (uint256) {
ensureAllowed("unlistMarket(address)");

Market storage _market = markets[market];

if (!_market.isListed) {
return fail(Error.MARKET_NOT_LISTED, FailureInfo.UNLIST_MARKET_NOT_LISTED);
}

require(actionPaused(market, Action.BORROW), "borrow action is not paused");
require(actionPaused(market, Action.MINT), "mint action is not paused");
require(actionPaused(market, Action.REDEEM), "redeem action is not paused");
require(actionPaused(market, Action.REPAY), "repay action is not paused");
require(actionPaused(market, Action.ENTER_MARKET), "enter market action is not paused");
require(actionPaused(market, Action.LIQUIDATE), "liquidate action is not paused");
require(actionPaused(market, Action.SEIZE), "seize action is not paused");
require(actionPaused(market, Action.TRANSFER), "transfer action is not paused");
require(actionPaused(market, Action.EXIT_MARKET), "exit market action is not paused");

require(borrowCaps[market] == 0, "borrow cap is not 0");
require(supplyCaps[market] == 0, "supply cap is not 0");

require(_market.collateralFactorMantissa == 0, "collateral factor is not 0");

_market.isListed = false;
emit MarketUnlisted(market);

return uint256(Error.NO_ERROR);
}

/**
* @notice Removes asset from sender's account liquidity calculation
* @dev Sender must not have an outstanding borrow balance in the asset,
Expand Down
12 changes: 5 additions & 7 deletions contracts/Comptroller/Diamond/facets/PolicyFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ contract PolicyFacet is IPolicyFacet, XVSRewardsHelper {
// Pausing is a very serious situation - we revert to sound the alarms
checkProtocolPauseState();
checkActionPauseState(vToken, Action.BORROW);

ensureListed(markets[vToken]);

uint256 borrowCap = borrowCaps[vToken];
require(borrowCap != 0, "market borrow cap is 0");

if (!markets[vToken].accountMembership[borrower]) {
// only vTokens may call borrowAllowed if borrower not in market
require(msg.sender == vToken, "sender must be vToken");
Expand All @@ -128,12 +130,8 @@ contract PolicyFacet is IPolicyFacet, XVSRewardsHelper {
return uint256(Error.PRICE_ERROR);
}

uint256 borrowCap = borrowCaps[vToken];
// Borrow cap of 0 corresponds to unlimited borrowing
if (borrowCap != 0) {
uint256 nextTotalBorrows = add_(VToken(vToken).totalBorrows(), borrowAmount);
require(nextTotalBorrows < borrowCap, "market borrow cap reached");
}
uint256 nextTotalBorrows = add_(VToken(vToken).totalBorrows(), borrowAmount);
require(nextTotalBorrows <= borrowCap, "market borrow cap reached");

(Error err, , uint256 shortfall) = getHypotheticalAccountLiquidityInternal(
borrower,
Expand Down
4 changes: 2 additions & 2 deletions contracts/Comptroller/Diamond/facets/SetterFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,9 @@ contract SetterFacet is ISetterFacet, FacetBase {

/**
* @notice Set the given borrow caps for the given vToken market Borrowing that brings total borrows to or above borrow cap will revert
* @dev Allows a privileged role to set the borrowing cap for a vToken market. A borrow cap of 0 corresponds to unlimited borrowing
* @dev Allows a privileged role to set the borrowing cap for a vToken market. A borrow cap of 0 corresponds to Borrow not allowed
* @param vTokens The addresses of the markets (tokens) to change the borrow caps for
* @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing
* @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to Borrow not allowed
*/
function _setMarketBorrowCaps(VToken[] calldata vTokens, uint256[] calldata newBorrowCaps) external {
ensureAllowed("_setMarketBorrowCaps(address[],uint256[])");
Expand Down
2 changes: 2 additions & 0 deletions contracts/Comptroller/Diamond/interfaces/IMarketFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ interface IMarketFacet {
function getAllMarkets() external view returns (VToken[] memory);

function updateDelegate(address delegate, bool allowBorrows) external;

function unlistMarket(address market) external returns (uint256);
}
3 changes: 2 additions & 1 deletion contracts/Utils/ErrorReporter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ contract ComptrollerErrorReporter {
SET_VAI_MINT_RATE_CHECK,
SET_VAICONTROLLER_OWNER_CHECK,
SET_MINTED_VAI_REJECTION,
SET_TREASURY_OWNER_CHECK
SET_TREASURY_OWNER_CHECK,
UNLIST_MARKET_NOT_LISTED
}

/**
Expand Down
34 changes: 34 additions & 0 deletions deploy/010-facet-upgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { DeployFunction } from "hardhat-deploy/types";
import { HardhatRuntimeEnvironment } from "hardhat/types";

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre;
const { deploy } = deployments;

const { deployer } = await getNamedAccounts();

await deploy("PolicyFacet", {
from: deployer,
args: [],
log: true,
autoMine: true,
});

await deploy("SetterFacet", {
from: deployer,
args: [],
log: true,
autoMine: true,
});

await deploy("MarketFacet", {
from: deployer,
args: [],
log: true,
autoMine: true,
});
};

func.tags = ["FacetUpgrade"];

export default func;
40 changes: 37 additions & 3 deletions deployments/bscmainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -13064,7 +13064,7 @@
]
},
"MarketFacet": {
"address": "0x9622522d94BdEe9b1d7C2CD944e3ed74B33BD9Cf",
"address": "0x4b093a3299F39615bA6b34B7897FDedCe7b83D63",
"abi": [
{
"anonymous": false,
Expand Down Expand Up @@ -13180,6 +13180,19 @@
"name": "MarketListed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "vToken",
"type": "address"
}
],
"name": "MarketUnlisted",
"type": "event"
},
{
"constant": false,
"inputs": [
Expand Down Expand Up @@ -13957,6 +13970,27 @@
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "market",
"type": "address"
}
],
"name": "unlistMarket",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
Expand Down Expand Up @@ -17306,7 +17340,7 @@
]
},
"PolicyFacet": {
"address": "0x95CC56f266BC95Ae2486cb0cFeda1054B4aA4086",
"address": "0x93e7Ff7c87B496aE76fFb22d437c9d46461A9B51",
"abi": [
{
"anonymous": false,
Expand Down Expand Up @@ -26413,7 +26447,7 @@
]
},
"SetterFacet": {
"address": "0x7dc9E7b21a9E343f4AD926b8B00Cff5adf5c1CdE",
"address": "0x9B0D9D7c50d90f23449c4BbCAA671Ce7cd19DbCf",
"abi": [
{
"anonymous": false,
Expand Down
276 changes: 160 additions & 116 deletions deployments/bscmainnet/MarketFacet.json

Large diffs are not rendered by default.

232 changes: 116 additions & 116 deletions deployments/bscmainnet/PolicyFacet.json

Large diffs are not rendered by default.

234 changes: 117 additions & 117 deletions deployments/bscmainnet/SetterFacet.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions deployments/bscmainnet_addresses.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@
"Liquidator_Implementation": "0xE26cE9b5FDd602225cCcC4cef7FAE596Dcf2A965",
"Liquidator_Proxy": "0x0870793286aada55d39ce7f82fb2766e8004cf43",
"MATIC": "0xCC42724C6683B7E57334c4E856f4c9965ED682bD",
"MarketFacet": "0x9622522d94BdEe9b1d7C2CD944e3ed74B33BD9Cf",
"MarketFacet": "0x4b093a3299F39615bA6b34B7897FDedCe7b83D63",
"MoveDebtDelegate": "0x89621C48EeC04A85AfadFD37d32077e65aFe2226",
"MoveDebtDelegate_Implementation": "0x8439932C45e646FcC1009690417A65BF48f68Ce7",
"MoveDebtDelegate_Proxy": "0x89621C48EeC04A85AfadFD37d32077e65aFe2226",
"PSMProxyAdmin": "0x3f918bA9446552AB184C6Ffd2e2fcB1FA5ee1e59",
"PegStability_USDT": "0xC138aa4E424D1A8539e8F38Af5a754a2B7c3Cc36",
"PegStability_USDT_Implementation": "0x9664568e5131e85f67d87fCD55B249F5D25fa43e",
"PegStability_USDT_Proxy": "0xC138aa4E424D1A8539e8F38Af5a754a2B7c3Cc36",
"PolicyFacet": "0x95CC56f266BC95Ae2486cb0cFeda1054B4aA4086",
"PolicyFacet": "0x93e7Ff7c87B496aE76fFb22d437c9d46461A9B51",
"Prime": "0xBbCD063efE506c3D42a0Fa2dB5C08430288C71FC",
"PrimeLiquidityProvider": "0x23c4F844ffDdC6161174eB32c770D4D8C07833F2",
"PrimeLiquidityProvider_Implementation": "0x208068AE8A619FCc851659791659B1aA40d796dA",
Expand All @@ -51,7 +51,7 @@
"Prime_Proxy": "0xBbCD063efE506c3D42a0Fa2dB5C08430288C71FC",
"RewardFacet": "0xc2F6bDCEa4907E8CB7480d3d315bc01c125fb63C",
"SXP": "0x47BEAd2563dCBf3bF2c9407fEa4dC236fAbA485A",
"SetterFacet": "0x7dc9E7b21a9E343f4AD926b8B00Cff5adf5c1CdE",
"SetterFacet": "0x9B0D9D7c50d90f23449c4BbCAA671Ce7cd19DbCf",
"SnapshotLens": "0x9802448Af040DA880Bf51bCbe4f2A4205ebC6d2C",
"SwapRouterCorePool": "0x8938E6dA30b59c1E27d5f70a94688A89F7c815a4",
"TRX": "0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3",
Expand Down
40 changes: 37 additions & 3 deletions deployments/bsctestnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -4520,7 +4520,7 @@
]
},
"MarketFacet": {
"address": "0xF2F2aE5480c4527787Fb7Cde1Ed9A3EdfD40A60d",
"address": "0x00a949FfDa9B216fBA9C4E5b40ef561Af0FDb723",
"abi": [
{
"anonymous": false,
Expand Down Expand Up @@ -4636,6 +4636,19 @@
"name": "MarketListed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "vToken",
"type": "address"
}
],
"name": "MarketUnlisted",
"type": "event"
},
{
"constant": false,
"inputs": [
Expand Down Expand Up @@ -5413,6 +5426,27 @@
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "market",
"type": "address"
}
],
"name": "unlistMarket",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
Expand Down Expand Up @@ -7826,7 +7860,7 @@
]
},
"PolicyFacet": {
"address": "0x7b17b28687B817158c20e3d1bf100106fBE794cf",
"address": "0x085C8d0133291348004AabFfbE7CAc2097aF2aa1",
"abi": [
{
"anonymous": false,
Expand Down Expand Up @@ -18178,7 +18212,7 @@
]
},
"SetterFacet": {
"address": "0xaBdE9599a4aEcE4fEC59fBF2b8445149bc8B2c70",
"address": "0x490DFD07f592452307817C4283866035BDb3b275",
"abi": [
{
"anonymous": false,
Expand Down
Loading

0 comments on commit 8c821ac

Please sign in to comment.