Comptroller
The Comptroller is the risk management layer of the CapyFi protocol; it determines how much collateral a user is required to maintain, and whether (and by how much) a user can be liquidated.
Introduction
Understanding the Comptroller
Each time a user interacts with a caToken, 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
Implementation details
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
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 cTokens) returns (uint[] memory)msg.sender: The account which shall enter the given markets.
cTokens: The addresses of the caToken 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 Example
Comptroller troll = Comptroller(0xABCD...);
CToken[] memory cTokens = new CToken[](2);
cTokens[0] = CErc20(0x3FDA...);
cTokens[1] = CEther(0x3FDB...);
uint[] memory errors = troll.enterMarkets(cTokens);Web3 1.0 Example
const troll = Comptroller.at(0xABCD...);
const cTokens = [CErc20.at(0x3FDA...), CEther.at(0x3FDB...)];
const errors = await troll.methods.enterMarkets(cTokens).send({from: ...});Exit Market
Exit a 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 cToken) returns (uint)msg.sender: The account which shall exit the given market.
cTokens: The addresses of the caToken market to exit.
RETURN: 0 on success, otherwise an Error code.
Solidity Example
Comptroller troll = Comptroller(0xABCD...);
uint error = troll.exitMarket(CToken(0x3FDA...));Web3 1.0 Example
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
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 Example
Comptroller troll = Comptroller(0xABCD...);
address[] memory markets = troll.getAssetsIn(0xMyAccount);Web3 1.0 Example
const troll = Comptroller.at(0xABCD...);
const markets = await troll.methods.getAssetsIn(cTokens).call();Collateral Factor
Get collateral factor for a caToken
A caToken's collateral factor can range from 0-90%, and represents the proportionate increase in liquidity (borrow limit) that an account receives by minting the caToken. 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. Collateral factors can be increased (or decreased) through protocol governance, as market conditions change.
Comptroller
function markets(address cTokenAddress) view returns (bool, uint, bool)cTokenAddress: The address of the caToken to check if listed and get the collateral factor for.
RETURN: Tuple of values (isListed, collateralFactorMantissa, isComped); isListed represents whether the comptroller recognizes this caToken; 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 governance tokens.
Solidity Example
Comptroller troll = Comptroller(0xABCD...);
(bool isListed, uint collateralFactorMantissa, bool isComped) = troll.markets(0x3FDA...);Web3 1.0 Example
const troll = Comptroller.at(0xABCD...);
const result = await troll.methods.markets(0x3FDA...).call();
const {0: isListed, 1: collateralFactorMantissa, 2: isComped} = result;Get Account Liquidity
Calculate account liquidity and shortfall
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 the CapyFi Protocol 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 Example
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 Example
const troll = Comptroller.at(0xABCD...);
const result = await troll.methods.getAccountLiquidity(0xBorrower).call();
const {0: error, 1: liquidity, 2: shortfall} = result;Close Factor
Percentage of borrow that can be repaid in a single liquidation
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 Example
Comptroller troll = Comptroller(0xABCD...);
uint closeFactor = troll.closeFactorMantissa();Web3 1.0 Example
const troll = Comptroller.at(0xABCD...);
const closeFactor = await troll.methods.closeFactorMantissa().call();Liquidation Incentive
Incentive for liquidators
The liquidation incentive is the additional collateral given to liquidators as a reward for liquidating undercollateralized accounts. This incentive ensures that liquidators are compensated for their service and helps maintain the health of the protocol.
Comptroller
function liquidationIncentiveMantissa() view returns (uint)RETURN: The liquidation incentive, scaled by 1e18, is multiplied by the amount of collateral seized to determine the total amount of collateral the liquidator receives.
Solidity Example
Comptroller troll = Comptroller(0xABCD...);
uint liquidationIncentive = troll.liquidationIncentiveMantissa();Web3 1.0 Example
const troll = Comptroller.at(0xABCD...);
const liquidationIncentive = await troll.methods.liquidationIncentiveMantissa().call();Key Events
Important events emitted by Comptroller contracts
MarketEntered
event MarketEntered(CToken cToken, address account)Emitted when an account enters a market
MarketExited
event MarketExited(CToken cToken, address account)Emitted when an account exits a market
NewCloseFactor
event NewCloseFactor(uint oldCloseFactorMantissa, uint newCloseFactorMantissa)Emitted when the close factor is updated
NewCollateralFactor
event NewCollateralFactor(CToken cToken, uint oldCollateralFactorMantissa, uint newCollateralFactorMantissa)Emitted when a collateral factor is updated
NewLiquidationIncentive
event NewLiquidationIncentive(uint oldLiquidationIncentiveMantissa, uint newLiquidationIncentiveMantissa)Emitted when the liquidation incentive is updated
Error Codes
Common error codes returned by Comptroller functions
Error Codes
| Code | Error | 
|---|---|
| 0 | NO_ERROR | 
| 1 | UNAUTHORIZED | 
| 2 | BAD_INPUT | 
| 3 | COMPTROLLER_REJECTION | 
| 4 | COMPTROLLER_CALCULATION_ERROR | 
| 5 | INTEREST_RATE_MODEL_ERROR | 
| 6 | INVALID_ACCOUNT_PAIR | 
| 7 | INVALID_CLOSE_AMOUNT_REQUESTED | 
| 8 | INVALID_COLLATERAL_FACTOR | 
| 9 | MATH_ERROR | 
| 10 | MARKET_NOT_FRESH | 
| 11 | MARKET_NOT_LISTED | 
| 12 | NONZERO_BORROW_BALANCE | 
| 13 | PRICE_ERROR | 
| 14 | REJECTION | 
| 15 | SNAPSHOT_ERROR | 
| 16 | TOO_MANY_ASSETS | 
| 17 | TOO_MUCH_REPAY | 
Failure Info
| Code | Error | 
|---|---|
| 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
Get information about all markets
The Comptroller contract has an array called `getAllMarkets` that contains the addresses of each caToken contract. Each address in the `getAllMarkets` array can be used to fetch a metadata struct in the Comptroller's markets constant.
Comptroller
CToken[] public getAllMarkets;Solidity Example
Comptroller troll = Comptroller(0xABCD...);
CToken cTokens[] = troll.getAllMarkets();Web3 1.2.6 Example
const comptroller = new web3.eth.Contract(comptrollerAbi, comptrollerAddress);
const cTokens = await comptroller.methods.getAllMarkets().call();
const cToken = cTokens[0]; // address of a cToken