Comptroller
This documentation is inspired by Compound protocol v2's developer documentation as we use the same codebase (commit a3214f67b73310d547e00fc578e8355911c9d376).
Comptroller
Introduction
The Comptroller is the risk management layer of Enclabs ; it determines how much collateral a user is required to maintain, and whether (and by how much) a user can be liquidated. Each time a user interacts with a vToken, the Comptroller is asked to approve or deny the transaction.
The Comptroller maps user balances to prices (via the Price Oracle) to risk weights (called Collateral Factors) to make its determinations. Users explicitly list which assets they would like included in their risk scoring, by calling Enter Markets and Exit Market.
Architecture
The Comptroller is implemented as an upgradeable proxy. The Unitroller proxies all logic to the Comptroller implementation, but storage values are set on the Unitroller. To call Comptroller functions, use the Comptroller ABI on the Unitroller address.
Enter Markets
Enter into a list of markets - it is not an error to enter the same market more than once. In order to supply collateral or borrow in a market, it must be entered first.
Comptroller
function enterMarkets(address[] calldata vTokens) returns (uint[] memory)msg.sender: The account which shall enter the given markets.vTokens: The addresses of the vToken markets to enter.RETURN: For each market, returns an error code indicating whether or not it was entered. Each is 0 on success, otherwise an Error code.
Solidity
Comptroller troll = Comptroller(0xABCD...);
VToken[] memory vTokens = new VToken[](2);
vTokens[0] = CErc20(0x3FDA...);
vTokens[1] = CEther(0x3FDB...);
uint[] memory errors = troll.enterMarkets(vTokens);Web3 1.0
const troll = Comptroller.at(0xABCD...);
const vTokens = [CErc20.at(0x3FDA...), CEther.at(0x3FDB...)];
const errors = await troll.methods.enterMarkets(vTokens).send({from: ...});Exit Market
Exit a market - it is not an error to exit a market which is not currently entered. Exited markets will not count towards account liquidity calculations.
Comptroller
function exitMarket(address vToken) returns (uint)msg.sender: The account which shall exit the given market.vTokens: The addresses of the vToken market to exit.RETURN: 0 on success, otherwise an Error code.
Solidity
Comptroller troll = Comptroller(0xABCD...);
uint error = troll.exitMarket(VToken(0x3FDA...));Web3 1.0
const troll = Comptroller.at(0xABCD...);
const errors = await troll.methods.exitMarket(CEther.at(0x3FDB...)).send({from: ...});Get Assets In
Get the list of markets an account is currently entered into. In order to supply collateral or borrow in a market, it must be entered first. Entered markets count towards account liquidity calculations.
Comptroller
function getAssetsIn(address account) view returns (address[] memory)
account: The account whose list of entered markets shall be queried.RETURN: The address of each market which is currently entered into.
Solidity
Comptroller troll = Comptroller(0xABCD...);
address[] memory markets = troll.getAssetsIn(0xMyAccount);Web3 1.0
const troll = Comptroller.at(0xABCD...);
const markets = await troll.methods.getAssetsIn(vTokens).call();Collateral Factor
A vToken’s collateral factor can range from 0-90%, and represents the proportionate increase in liquidity (borrow limit) that an account receives by minting the vToken. Generally, large or liquid assets have high collateral factors, while small or illiquid assets have low collateral factors. If an asset has a 0% collateral factor, it can’t be used as collateral (or seized in liquidation), though it can still be borrowed.
Comptroller
function markets(address vTokenAddress) view returns (bool, uint, bool)vTokenAddress: The address of the vToken to check if listed and get the collateral factor for.RETURN: Tuple of values (isListed, collateralFactorMantissa, isComped); isListed represents whether the comptroller recognizes this vToken; collateralFactorMantissa, scaled by 1e18, is multiplied by a supply balance to determine how much value can be borrowed. The isComped boolean indicates whether or not suppliers and borrowers are distributed COMP tokens.
Solidity
Comptroller troll = Comptroller(0xABCD...);
(bool isListed, uint collateralFactorMantissa, bool isComped) = troll.markets(0x3FDA...);Web3 1.0
const troll = Comptroller.at(0xABCD...);
const result = await troll.methods.markets(0x3FDA...).call();
const {0: isListed, 1: collateralFactorMantissa, 2: isComped} = result;Get Account Liquidity
Account Liquidity represents the USD value borrowable by a user, before it reaches liquidation. Users with a shortfall (negative liquidity) are subject to liquidation, and can’t withdraw or borrow assets until Account Liquidity is positive again.
For each market the user has entered into, their supplied balance is multiplied by the market’s collateral factor, and summed; borrow balances are then subtracted, to equal Account Liquidity. Borrowing an asset reduces Account Liquidity for each USD borrowed; withdrawing an asset reduces Account Liquidity by the asset’s collateral factor times each USD withdrawn.
Because Enclabas finance exclusively uses unsigned integers, Account Liquidity returns either a surplus or shortfall.
Comptroller
function getAccountLiquidity(address account) view returns (uint, uint, uint)account: The account whose liquidity shall be calculated.RETURN: Tuple of values (error, liquidity, shortfall). The error shall be 0 on success, otherwise an error code. A non-zero liquidity value indicates the account has available account liquidity. A non-zero shortfall value indicates the account is currently below his/her collateral requirement and is subject to liquidation. At most one of liquidity or shortfall shall be non-zero.
Solidity
Comptroller troll = Comptroller(0xABCD...);
(uint error, uint liquidity, uint shortfall) = troll.getAccountLiquidity(msg.caller);
require(error == 0, "join the Discord");
require(shortfall == 0, "account underwater");
require(liquidity > 0, "account has excess collateral");Web3 1.0
const troll = Comptroller.at(0xABCD...);
const result = await troll.methods.getAccountLiquidity(0xBorrower).call();
const {0: error, 1: liquidity, 2: shortfall} = result;Close Factor
The percent, ranging from 0% to 100%, of a liquidatable account’s borrow that can be repaid in a single liquidate transaction. If a user has multiple borrowed assets, the closeFactor applies to any single borrowed asset, not the aggregated value of a user’s outstanding borrowing.
Comptroller
function closeFactorMantissa() view returns (uint)RETURN: The closeFactor, scaled by 1e18, is multiplied by an outstanding borrow balance to determine how much could be closed.
Solidity
Comptroller troll = Comptroller(0xABCD...);
uint closeFactor = troll.closeFactorMantissa();Web3 1.0
const troll = Comptroller.at(0xABCD...);
const closeFactor = await troll.methods.closeFactorMantissa().call();Liquidation Incentive
The additional collateral given to liquidators as an incentive to perform liquidation of underwater accounts. A portion of this is given to the collateral vToken reserves as determined by the seize share. The seize share is assumed to be 0 if the vToken does not have a protocolSeizeShareMantissa constant. For example, if the liquidation incentive is 1.08, and the collateral’s seize share is 1.028, liquidators receive an extra 5.2% of the borrower’s collateral for every unit they close, and the remaining 2.8% is added to the vToken’s reserves.
Comptroller
function liquidationIncentiveMantissa() view returns (uint)RETURN: The liquidationIncentive, scaled by 1e18, is multiplied by the closed borrow amount from the liquidator to determine how much collateral can be seized.
Solidity
Comptroller troll = Comptroller(0xABCD...);
uint closeFactor = troll.liquidationIncentiveMantissa();Web3 1.0
const troll = Comptroller.at(0xABCD...);
const closeFactor = await troll.methods.liquidationIncentiveMantissa().call();Key Events
MarketEntered(VToken vToken, address account)
Emitted upon a successful Enter Market.
MarketExited(VToken vToken, address account)
Emitted upon a successful Exit Market.
Error Codes
0
NO_ERROR
Not a failure.
1
UNAUTHORIZED
The sender is not authorized to perform this action.
2
COMPTROLLER_MISMATCH
Liquidation cannot be performed in markets with different comptrollers.
3
INSUFFICIENT_SHORTFALL
The account does not have sufficient shortfall to perform this action.
4
INSUFFICIENT_LIQUIDITY
The account does not have sufficient liquidity to perform this action.
5
INVALID_CLOSE_FACTOR
The close factor is not valid.
6
INVALID_COLLATERAL_FACTOR
The collateral factor is not valid.
7
INVALID_LIQUIDATION_INCENTIVE
The liquidation incentive is invalid.
8
MARKET_NOT_ENTERED
The market has not been entered by the account.
9
MARKET_NOT_LISTED
The market is not currently listed by the comptroller.
10
MARKET_ALREADY_LISTED
An admin tried to list the same market more than once.
11
MATH_ERROR
A math calculation error occurred.
12
NONZERO_BORROW_BALANCE
The action cannot be performed since the account carries a borrow balance.
13
PRICE_ERROR
The comptroller could not obtain a required price of an asset.
14
REJECTION
The comptroller rejects the action requested by the market.
15
SNAPSHOT_ERROR
The comptroller could not get the account borrows and exchange rate from the market.
16
TOO_MANY_ASSETS
Attempted to enter more markets than are currently supported.
17
TOO_MUCH_REPAY
Attempted to repay more than is allowed by the protocol.
Failure Info
0
ACCEPT_ADMIN_PENDING_ADMIN_CHECK
1
ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK
2
EXIT_MARKET_BALANCE_OWED
3
EXIT_MARKET_REJECTION
4
SET_CLOSE_FACTOR_OWNER_CHECK
5
SET_CLOSE_FACTOR_VALIDATION
6
SET_COLLATERAL_FACTOR_OWNER_CHECK
7
SET_COLLATERAL_FACTOR_NO_EXISTS
8
SET_COLLATERAL_FACTOR_VALIDATION
9
SET_COLLATERAL_FACTOR_WITHOUT_PRICE
10
SET_IMPLEMENTATION_OWNER_CHECK
11
SET_LIQUIDATION_INCENTIVE_OWNER_CHECK
12
SET_LIQUIDATION_INCENTIVE_VALIDATION
13
SET_MAX_ASSETS_OWNER_CHECK
14
SET_PENDING_ADMIN_OWNER_CHECK
15
SET_PENDING_IMPLEMENTATION_OWNER_CHECK
16
SET_PRICE_ORACLE_OWNER_CHECK
17
SUPPORT_MARKET_EXISTS
18
SUPPORT_MARKET_OWNER_CHECK
Market Metadata
The Comptroller contract has an array called getAllMarkets that contains the addresses of each vToken contract. Each address in the getAllMarkets array can be used to fetch a metadata struct in the Comptroller’s markets constant. See the Comptroller Storage contract for the Market struct definition.
Comptroller
VToken[] public getAllMarkets;Solidity
Comptroller troll = Comptroller(0xABCD...);
VToken vTokens[] = troll.getAllMarkets();Web3 1.2.6
const comptroller = new web3.eth.Contract(comptrollerAbi, comptrollerAddress);
const vTokens = await comptroller.methods.getAllMarkets().call();
const vToken = vTokens[0]; // address of a vTokenLast updated