Cozy tokens

API reference for Cozy tokens. Cozy tokens are a core contract in the Cozy protocol and each market is a unique instance of a Cozy token.

Core methods

This section contains the primary methods for interacting with the Cozy protocol. Most methods in this section return a uint256 value of zero (0) for a successful call, or return an error code on a unsuccessful call. The main exceptions to this general rule are the methods that are part of the standard ERC-20 specification, such as the transfer and balanceOf methods.

Mint

You use the mint function to transfer assets from your wallet to any Cozy market. You can use the mint function when you want to do the following:

  • supply funds to use as collateral for protected borrowing.

  • deposit funds to provide protection and earn interest.

Depending on whether you are minting tokens or ETH, use one of the following function signatures:

// For markets with tokens as underlying
function mint(uint256 amount) external returns (uint256);

// For markets with ETH as underlying
function mint() external payable;

For tokens, the amount is the number of tokens to supply, denominated in units of the underlying token. For ETH, instead of specifying an amount, you send ETH along with the transaction and msg.value is used in place of amount.

Before supplying a token, remember to approve the Cozy market. A successful mint call returns a value of zero and transfers Cozy tokens to the caller's wallet. The number of Cozy tokens the user receives is equal to the supplied amount divided by the exchange rate.

Redeem

You use the redeem method to redeem Cozy tokens for the underlying supplying funds. The amount of underlying received is equal to the number of Cozy tokens redeemed multiplied by the exchange rate. There are two methods you can use to redeem tokens:

// Specify amount to redeem denominated in amount of Cozy tokens
function redeem(uint256 redeemTokens) external returns (uint256);

// Specify amount to redeem denominated in amount of underlying
function redeemUnderlying(uint256 redeemAmount) external returns (uint256);

A successful redeem burns the caller's Cozy tokens, transfers a proportional amount of the underlying asset, and returns a value of zero.

Borrow

After supplying collateral with the mint method, you can use the borrow method to borrow funds from the Cozy protocol. Borrowed funds accrue interest at the current borrow rate. If a borrower has insufficient account liquidity, however, the borrowed funds can be liquidated.

The method for borrowing funds from a market is:

function borrow(uint256 borrowAmount) external returns (uint256);

where borrowAmount is specified in units of the token to borrow.

Repay debt

You can use either the repayBorrow or repayBorrowBehalf function to repay borrowed funds as follows:

// --- For markets with tokens as underlying ---
// Repays `repayAmount` of the underlying for `msg.sender`
function repayBorrow(uint256 repayAmount) external returns (uint256);

// Repays `repayAmount` of the underlying for `borrower`
function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256);

// --- For markets with ETH as underlying ---
// Repays `msg.value` ETH for `msg.sender`
function repayBorrow() external payable;

// Repays `msg.value` ETH for `borrower`
function repayBorrowBehalf(address borrower) external payable;

To repay all token debt without leaving any dust, you can specify a repayAmount ofMAX_UINT256 = 2^256 - 1.

To repay all ETH debt without leaving any dust, it is recommended to use a special helper contract called Maximillion and call the following method:

function repayBehalfExplicit(address borrower, CEther cEther_) external payable;

where borrower is the address of the user to pay back debt for, and cEther_ is the address of the market to pay back debt in (for example, the Cozy Ether money market, or a protection market with ETH as the underlying). When calling this method, send along more ETH than the debt amount, and after paying off the full debt, the excess ETH will be refunded.

The Maximillion contract is deployed on mainnet at 0xf859A1AD94BcF445A406B892eF0d3082f4174088.

Liquidate

You use the liquidateBorrow function to liquidate a user's assets when their account liquidity is negative. This action brings the user back to a solvent state. During a liquidation, the liquidator repays some or all of the user's borrow and, in return, receives a portion of their collateral, as Cozy tokens, at a discount.

The maximum portion of the borrow that can be liquidated is specified by the close factor in the Comptroller. As with mints and repays, liquidating pulls tokens from the caller, so the liquidator must approve the Cozy token contract before liquidating.

Liquidations can be executed with the following methods:

// For markets with tokens as underlying
function liquidateBorrow(
    address borrower,
    uint256 repayAmount,
    CTokenInterface cTokenCollateral
) external returns (uint256);

// For markets with ETH as underlying
function liquidateBorrow(
    address borrower,
    CToken cTokenCollateral
) external payable;

where borrower is the account with a shortfall to liquidate and cTokenCollateral is the token being used by the borrower as collateral that the liquidator will receive. For token-based markets, repayAmount specifies how much of the borrowed asset to repay, and for ETH-based markets that amount is specified by msg.value.

Transfer

All Cozy tokens are ERC-20 compliant and can be transferred with both the transfer and transferFrom functions. They also implement ERC-2612 permit function to improve the approval flow.

Token transfers fail if the transfer would put the account into an under-collateralized position.

View methods

This section contains the primary methods for viewing information about Cozy markets.

Exchange rate

Every Cozy token has it's own exchange rate, which is a function of the market's cash, borrows, reserves, and supply. This value is initialized to 0.02 when a market is created, and increases as the accrues interest. The exchange rate for a given market is calculated as:

exchangeRate = ( getCash() + totalBorrows() - totalReserves() ) / totalSupply();

The exchange rate is represented as a mantissa (an 18 decimal number) with a precision of 18 + underlyingDecimals - cozyTokenDecimals. For example, all Cozy tokens have 8 decimals. If the underlying token has 6 decimals, the initial exchange rate for that market would have 18 + 6 - 8 = 16 decimals. Querying the exchange rate of this market immediately after deployment would return 0.02e16 = 200000000000000.

Cash

Cash is the amount of the underlying token held by the Cozy token. You can read the cash held by the contract using the following function:

function getCash() external view returns (uint256);

For markets with tokens as the underlying, this function returns the value of underlyingToken.balanceOf(address(this));. For markets with ETH as the underlying, the function returns address(this).balance - msg.value.

Borrows

A market's total borrows is the amount of the underlying token actively borrowed. This amount is used when computing the accrued interest. It can be queried for a given market with the function below. When a market is triggered, this value is set to zero, so the below methods always returns zero after a trigger event.

// View method which returns the last known borrow amount, `totalBorrows`
function totalBorrows() external view returns (uint256);

// Non-view method which accrues interest before returning `totalBorrows`.
// Because this version accrues interest first, it will be more up to date
function totalBorrowsCurrent() external returns (uint256);

The amount being borrowed by an individual user can be read with the method below. Both methods always returns a borrow balance of zero after a market is triggered.

// View method which returns the last known borrow balance for `account`
function borrowBalanceStored(address account) external view returns (uint256);

// Non-view method which accrues interest before returning `borrowBalanceStored`.
// Because this version accrues interest first, it will be more up to date
function borrowBalanceCurrent(address account) external returns (uint256);

Supply

A market's total supply is the number of Cozy tokens that have been minted and are currently in circulation. You can use the standard ERC-20 totalSupply method to return the number of Cozy tokens in a market as follows:

function totalSupply() external view returns (uint256);

Balances

To read a user's Cozy token balance, you can use the standard ERC-20 balanceOf method as follows:

function balanceOf(address account) external view returns (uint256);

You can also read a user's balance with the balanceOfUnderlying function, which returns the user's Cozy token balance multiplied by the exchange rate.

// Note that `balanceOfUnderlying` is not a view method
function balanceOfUnderlying(address account) external returns (uint256);

For example, if a user supplies 1000 USDC with an exchange rate of 0.02, she receives 1000 / 0.02 = 50,000 Cozy tokens. Calling balanceOf will return 50,000e8, and calling balanceOfUnderlying will return 1000e6, since Cozy tokens have 8 decimals and USDC has 6 decimals).

Interest rates

You can use the borrowRatePerBlock function to read the current interest rate accrued by borrowers every block.

function borrowRatePerBlock() external view returns (uint256);

The supply-rate-per-block is a function of the borrow-rate-per-block, as well the market's reserve factor and the its utilization rate. The utilization rate is the ratio of borrowed funds to supplied funds, calculated as borrows / (cash + borrows - reserves). The supply rate is then calculated as utilizationRate * (borrowRatePerBlock * (1 - reserveFactor)). You can query this value as follows:

function supplyRatePerBlock() external view returns (uint256);

Both the borrow rate and supply rate returned from these methods will always be zero after a market is triggered.

Reserves

A portion of interest accrued from borrowers is set aside as cash, and cash from this source is known as reserves. The amount of reserves set aside is determined by the reserve factor. You can use the reserveFactorMantissa method to return the reserve factor. See the Interest Rates section for more information on how the reserve factor is used.

// Returns a mantissa, so it's scaled by 1e18
function reserveFactorMantissa() external view returns (uint256)

The total reserves in a given market can be read with:

function totalReserves() external view returns (uint256)

Reserves can be withdrawn or transferred by the protocol administrator.

Events

This sections contains the core events emitted by Cozy token contracts, and is not a comprehensive list.

// Emitted when interest is accrued
event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);

// Emitted when tokens are minted
event Mint(address minter, uint256 mintAmount, uint256 mintTokens);

// Emitted when tokens are redeemed
event Redeem(address redeemer, uint256 redeemAmount, uint256 redeemTokens);

// Emitted when underlying is borrowed
event Borrow(address borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);

// Emitted when a borrow is repaid
event RepayBorrow(address payer, address borrower, uint256 repayAmount, uint256 accountBorrows, uint256 totalBorrows);

// Emitted when a borrow is liquidated
event LiquidateBorrow(
  address liquidator,
  address borrower,
  uint256 repayAmount,
  address cTokenCollateral,
  uint256 seizeTokens
);

// Emitted when trigger status is set. This is emitted once at
// construction and one more time when the trigger is toggled
event TriggerSet(bool isTriggered);

Error handling

This section contains information about error codes and failure messages that may be returned when calling methods on a Cozy token contract.

Error codes

The table below contains a list of error codes that may be returned when calling methods on a Cozy token contract.

CodeNameDescription

0

NO_ERROR

Call succeeded

1

UNAUTHORIZED

Caller is not authorized

2

BAD_INPUT

Caller provided bad arguments

3

COMPTROLLER_REJECTION

The Comptroller blocked this action

4

COMPTROLLER_CALCULATION_ERROR

A calculation failed in the Comptroller

5

INTEREST_RATE_MODEL_ERROR

An invalid value was returned from the interest rate model

6

INVALID_ACCOUNT_PAIR

The borrower and liquidator are the same account

7

INVALID_CLOSE_AMOUNT_REQUESTED

The MAX_UINT256 is passed as the repayAmount during liquidation

8

INVALID_COLLATERAL_FACTOR

Provided collateral factor is not allowed

9

MATH_ERROR

An error occurred in a math operation

10

MARKET_NOT_FRESH

The market has not properly accrued interest

11

MARKET_NOT_LISTED

The market is not supported by the Comptroller

12

TOKEN_INSUFFICIENT_ALLOWANCE

Caller has not approved the contract to spend enough tokens

13

TOKEN_INSUFFICIENT_BALANCE

Caller has an insufficient token balance to complete the transaction

14

TOKEN_INSUFFICIENT_CASH

The market does not have enough cash at this time

15

TOKEN_TRANSFER_IN_FAILED

ERC-20 token transfer to the market failed

16

TOKEN_TRANSFER_OUT_FAILED

ERC-20 token transfer from the market failed

17

INVALID_GUARDIAN

Reserve guardian cannot be the zero address

Failure codes and logged messages

The table below contains a list of error codes that may be emitted in Failure logs when calling methods on a Cozy token contract. These codes are descriptive so no additional description is provided.

CodeName

0

ACCEPT_ADMIN_PENDING_ADMIN_CHECK

1

ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED

2

ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED

3

ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED

4

ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED

5

ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED

6

ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED

7

BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED

8

BORROW_ACCRUE_INTEREST_FAILED

9

BORROW_CASH_NOT_AVAILABLE

10

BORROW_FRESHNESS_CHECK

11

BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED

12

BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED

13

BORROW_MARKET_NOT_LISTED

14

BORROW_COMPTROLLER_REJECTION

15

LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED

16

LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED

17

LIQUIDATE_COLLATERAL_FRESHNESS_CHECK

18

LIQUIDATE_COMPTROLLER_REJECTION

19

LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED

20

LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX

21

LIQUIDATE_CLOSE_AMOUNT_IS_ZERO

22

LIQUIDATE_FRESHNESS_CHECK

23

LIQUIDATE_LIQUIDATOR_IS_BORROWER

24

LIQUIDATE_REPAY_BORROW_FRESH_FAILED

25

LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED

26

LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED

27

LIQUIDATE_SEIZE_COMPTROLLER_REJECTION

28

LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER

29

LIQUIDATE_SEIZE_TOO_MUCH

30

MINT_ACCRUE_INTEREST_FAILED

31

MINT_COMPTROLLER_REJECTION

32

MINT_EXCHANGE_CALCULATION_FAILED

33

MINT_EXCHANGE_RATE_READ_FAILED

34

MINT_FRESHNESS_CHECK

35

MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED

36

MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED

37

MINT_TRANSFER_IN_FAILED

38

MINT_TRANSFER_IN_NOT_POSSIBLE

39

REDEEM_ACCRUE_INTEREST_FAILED

40

REDEEM_COMPTROLLER_REJECTION

41

REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED

42

REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED

43

REDEEM_EXCHANGE_RATE_READ_FAILED

44

REDEEM_FRESHNESS_CHECK

45

REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED

46

REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED

47

REDEEM_TRANSFER_OUT_NOT_POSSIBLE

48

REDUCE_RESERVES_ACCRUE_INTEREST_FAILED

49

REDUCE_RESERVES_ADMIN_CHECK

50

REDUCE_RESERVES_CASH_NOT_AVAILABLE

51

REDUCE_RESERVES_FRESH_CHECK

52

REDUCE_RESERVES_VALIDATION

53

REPAY_BEHALF_ACCRUE_INTEREST_FAILED

54

REPAY_BORROW_ACCRUE_INTEREST_FAILED

55

REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED

56

REPAY_BORROW_COMPTROLLER_REJECTION

57

REPAY_BORROW_FRESHNESS_CHECK

58

REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED

59

REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED

60

REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE

61

SET_COLLATERAL_FACTOR_OWNER_CHECK

62

SET_COLLATERAL_FACTOR_VALIDATION

63

SET_COMPTROLLER_OWNER_CHECK

64

SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED

65

SET_INTEREST_RATE_MODEL_FRESH_CHECK

66

SET_INTEREST_RATE_MODEL_OWNER_CHECK

67

SET_MAX_ASSETS_OWNER_CHECK

68

SET_ORACLE_MARKET_NOT_LISTED

69

SET_PENDING_ADMIN_OWNER_CHECK

70

SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED

71

SET_RESERVE_FACTOR_ADMIN_CHECK

72

SET_RESERVE_FACTOR_FRESH_CHECK

73

SET_RESERVE_FACTOR_BOUNDS_CHECK

74

TRANSFER_COMPTROLLER_REJECTION

75

TRANSFER_NOT_ALLOWED

76

TRANSFER_NOT_ENOUGH

77

TRANSFER_TOO_MUCH

78

ADD_RESERVES_ACCRUE_INTEREST_FAILED

79

ADD_RESERVES_FRESH_CHECK

80

ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE

81

REDUCE_RESERVES_GUARDIAN_NOT_SET

82

TRIGGER_ACTIVATED_BEFORE_REDEEM_OR_BORROW

Last updated