Introduction

The Comptroller is the risk management layer of the DeBorrow protocol; 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 cToken, 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 cTokens) returns (uint[] memory)
  • msg.sender : The account which shall enter the given markets.
  • cTokens : The addresses of the cToken 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 codes.

Solidity

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

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 - 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 cToken market to exit.
  • RETURN : 0 on success, otherwise an Error codes.

Solidity

Comptroller troll = Comptroller(0xABCD...);
uint error = troll.exitMarket(CToken(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(cTokens).call();

Collateral Factor

A cToken's collateral factor can range from 0-90%, and represents the proportionate increase in liquidity (borrow limit) that an account receives by minting the cToken.

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 DeBorrow Governance, as market conditions change.

Comptroller

function markets(address cTokenAddress) view returns (bool, uint)
  • cTokenAddress : The address of the cToken to check if listed and get the collateral factor for.
  • RETURN : Tuple of values (isListed, collateralFactorMantissa); isListed represents whether the comptroller recognizes this cToken; collateralFactorMantissa, scaled by 1e18, is multiplied by a supply balance to determine how much value can be borrowed.

Solidity

Comptroller troll = Comptroller(0xABCD...);
(bool isListed, uint collateralFactorMantissa) = troll.markets(0x3FDA...);

Web3 1.0

const troll = Comptroller.at(0xABCD...);
const result = await troll.methods.markets(0x3FDA...).call();
const {0: isListed, 1: collateralFactorMantissa} = result;

Get Account Liquidity

Within the DeBorrow Protocol, account liquidity is defined as the total estimated ether value of an account's collateral (supply balances multiplied by the protocol collateral factor, minus the total value of that account's borrow balances. These values are calculated using only the markets which the account has entered into.

Users who do not have positive account liquidity do not have the ability to withdraw or borrow any assets until they bring their account liquidity back positive by supplying more assets or paying back outstanding borrows.

Sometimes account liquidity refers to a single signed value, rather than two unsigned values - however the DeBorrow Protocol only deals with unsigned integers. A negative value for account liquidity also means the user is subject to liquidation to bring their account liquidity back to zero.

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.closeFactoreMantissa().call();

Liquidation Incentive

The additional collateral given to liquidators as an incentive to perform liquidation of underwater accounts. For example, if the liquidation incentive is 1.1, liquidators receive an extra 10% of the borrowers collateral for every unit they close.

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

EventDescription
MarketEntered(CToken cToken, address account)Emitted upon a successful Enter Market.
MarketExited(CToken cToken, address account)Emitted upon a successful Exit Market.

Error Codes

CodeNameDescription
0NO_ERRORNot a failure.
1UNAUTHORIZEDThe sender is not authorized to perform this action.
2DEBOTROLLER_MISMATCHLiquidation cannot be performed in markets with different comptrollers.
3INSUFFICIENT_SHORTFALLThe account does not have sufficient shortfall to perform this action.
4INSUFFICIENT_LIQUIDITYThe account does not have sufficient liquidity to perform this action.
5INVALID_CLOSE_FACTORThe close factor is not valid.
6INVALID_COLLATERAL_FACTORThe collateral factor is not valid.
7INVALID_LIQUIDATION_INCENTIVEThe liquidation incentive is invalid.
8MARKET_NOT_ENTEREDThe market has not been entered by the account.
9MARKET_NOT_LISTEDThe market is not currently listed by the comptroller.
10MARKET_ALREADY_LISTEDAn admin tried to list the same market more than once.
11MATH_ERRORA math calculation error occurred.
12NONZERO_BORROW_BALANCEThe action cannot be performed since the account carries a borrow balance.
13PRICE_ERRORThe comptroller could not obtain a required price of an asset.
14REJECTIONThe comptroller rejects the action requested by the market.
15SNAPSHOT_ERRORThe comptroller could not get the account borrows and exchange rate from the market.
16TOO_MANY_ASSETSAttempted to enter more markets than are currently supported.
17TOO_MUCH_REPAYAttempted to repay more than is allowed by the protocol.

Failure Info

CodeName
0ACCEPT_ADMIN_PENDING_ADMIN_CHECK
1ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK
2EXIT_MARKET_BALANCE_OWED
3EXIT_MARKET_REJECTION
4SET_CLOSE_FACTOR_OWNER_CHECK
5SET_CLOSE_FACTOR_VALIDATION
6SET_COLLATERAL_FACTOR_OWNER_CHECK
7SET_COLLATERAL_FACTOR_NO_EXISTS
8SET_COLLATERAL_FACTOR_VALIDATION
9SET_COLLATERAL_FACTOR_WITHOUT_PRICE
10SET_IMPLEMENTATION_OWNER_CHECK
11SET_LIQUIDATION_INCENTIVE_OWNER_CHECK
12SET_LIQUIDATION_INCENTIVE_VALIDATION
13SET_MAX_ASSETS_OWNER_CHECK
14SET_PENDING_ADMIN_OWNER_CHECK
15SET_PENDING_IMPLEMENTATION_OWNER_CHECK
16SET_PRICE_ORACLE_OWNER_CHECK
17SUPPORT_MARKET_EXISTS
18SUPPORT_MARKET_OWNER_CHECK

DEBO Distribution Speeds

DEBO Speed

The “DEBO speed” unique to each market is an unsigned integer that specifies the amount of DEBO that is distributed, per block, to suppliers and borrowers in each market. As market conditions change, DEBO speeds are updated to ensure DEBO is distributed proportional to the utility of each market. Any user can call the Comptroller’s refreshCompSpeeds method at any time in order to update market DEBO speeds.

The following is the formula for calculating the rate that DEBO is distributed to each supported market.

interestPerBlock = cTokenBorrowRatePerBlock * cTokenTotalBorrows
utility = interestPerBlock * assetPrice
utilityFraction = utility / sumOfAllDEBOedMarketUtilities
marketCompSpeed = compRate * utilityFraction

Comptroller

function refreshCompSpeeds(address account) public
  • RETURN : None
  • events : CompSpeedUpdated - An event is emitted for each cToken with the address of the cToken and the new DEBO distribution speed per block.

Solidity

Comptroller troll = Comptroller(0xABCD...);
troll.refreshCompSpeeds();

Web3 1.2.6

const comptroller = new web3.eth.Contract(comptrollerAbi, comptrollerAddress);
await comptroller.methods.refreshCompSpeeds().send({ from: sender });

DEBO Distributed Per Block (All Markets)

The Comptroller Storage contract’s compRate is an unsigned integer that indicates the rate at which the protocol distributes DEBO to markets’ suppliers or borrowers, every Ethereum block. The value is the amount of DEBO (in wei), per block, allocated for the markets. Note that not every market has DEBO distributed to its participants (see Market Metadata).

The compRate indicates how much DEBO goes to the suppliers or borrowers, so doubling this number shows how much DEBO goes to all suppliers and borrowers combined. The code examples implement reading the amount of DEBO distributed, per Ethereum block, to all markets.

Comptroller

uint public compRate;

Solidity

Comptroller troll = Comptroller(0xABCD...);
// DEBO issued per block to suppliers OR borrowers * (1 * 10 ^ 18)
uint compRate = troll.compRate();
// Approximate DEBO issued per day to suppliers OR borrowers * (1 * 10 ^ 18)
uint compRatePerDay = compRate * 4 * 60 * 24;
// Approximate DEBO issued per day to suppliers AND borrowers * (1 * 10 ^ 18)
uint compRatePerDayTotal = compRatePerDay * 2;

Web3 1.2.6

const comptroller = new web3.eth.Contract(comptrollerAbi, comptrollerAddress);
let compRate = await comptroller.methods.compRate().call();
compRate = compRate / 1e18;
// DEBO issued to suppliers OR borrowers
const compRatePerDay = compRate * 4 * 60 * 24;
// DEBO issued to suppliers AND borrowers
const compRatePerDayTotal = compRatePerDay * 2;

DEBO Distributed Per Block (Single Market)

The Comptroller Storage contract has a mapping called compSpeeds. It maps cToken addresses to an integer of each market’s DEBO distribution per Ethereum block. The integer indicates the rate at which the protocol distributes DEBO to markets’ suppliers or borrowers. The value is the amount of DEBO (in wei), per block, allocated for the market. Note that not every market has DEBO distributed to its participants (see Market Metadata).

The speed indicates how much DEBO goes to the suppliers or the borrowers, so doubling this number shows how much DEBO goes to market suppliers and borrowers combined. The code examples implement reading the amount of DEBO distributed, per Ethereum block, to a single market.

Comptroller

mapping(address => uint) public compSpeeds;

Solidity

Comptroller troll = Comptroller(0x123...);
address cToken = 0xabc...;
// DEBO issued per block to suppliers OR borrowers * (1 * 10 ^ 18)
uint compSpeed = troll.compSpeeds(cToken);
// Approximate DEBO issued per day to suppliers OR borrowers * (1 * 10 ^ 18)
uint compSpeedPerDay = compSpeed * 4 * 60 * 24;
// Approximate DEBO issued per day to suppliers AND borrowers * (1 * 10 ^ 18)
uint compSpeedPerDayTotal = compSpeedPerDay * 2;

Web3 1.2.6

const cTokenAddress = '0xabc...';
const comptroller = new web3.eth.Contract(comptrollerAbi, comptrollerAddress);
let compSpeed = await comptroller.methods.compSpeeds(cTokenAddress).call();
compSpeed = compSpeed / 1e18;
// DEBO issued to suppliers OR borrowers
const compSpeedPerDay = compSpeed * 4 * 60 * 24;
// DEBO issued to suppliers AND borrowers
const compSpeedPerDayTotal = compSpeedPerDay * 2;

Claim DEBO

Every DeBorrow user accrues DEBO for each block they are supplying to or borrowing from the protocol. The protocol automatically transfers accrued DEBO to a user’s address when the total amount of DEBO accrued to that address (in a market) is greater than the claimCompThreshold, and the address executes any of the mint, borrow, transfer, liquidateBorrow, repayBorrow, or redeem functions on that market. Separately, users may call the claimComp method on any cToken contract at any time for finer grained control over which markets to claim from.

Comptroller

// Claim all the DEBO accrued by holder in all markets
function claimComp(address holder) public
// Claim all the DEBO accrued by holder in specific markets
function claimComp(address holder, CToken[] memory cTokens) public
// Claim all the DEBO accrued by specific holders in specific markets for their supplies and/or borrows
function claimComp(address[] memory holders, CToken[] memory cTokens, bool borrowers, bool suppliers) public

Solidity

Comptroller troll = Comptroller(0xABCD...);
troll.claimComp(0x1234...);

Web3 1.2.6

const comptroller = new web3.eth.Contract(comptrollerAbi, comptrollerAddress);
await comptroller.methods.claimComp("0x1234...").send({ from: sender });

Market Metadata

The Comptroller contract has an array called allMarkets that contains the addresses of each cToken contract. Each address in the allMarkets 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

CToken[] public allMarkets;

Solidity

Comptroller troll = Comptroller(0xABCD...);
CToken cTokens[] = troll.allMarkets();

Web3 1.2.6

const comptroller = new web3.eth.Contract(comptrollerAbi, comptrollerAddress);
const cTokens = await comptroller.methods.allMarkets().call();
const cToken = cTokens[0]; // address of a cToken