Skip to content

πŸ“Š Lend and borrow tokens on Compound. Liquidate undercollateralized borrowers

Notifications You must be signed in to change notification settings

Aboudoc/Compound

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

47 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Contributors Forks Stargazers Issues MIT License LinkedIn


Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. Supply and Redeem Contract
  5. State Variables
  6. Constructor
  7. Function supply
  8. Function getCTokenBalance
  9. Function getInfo
  10. Function estimateBalanceOfUnderlying
  11. Function balanceOfUnderlying
  12. Function redeem
  13. Test supply/redeem
  14. Borrow and Repay Contract
  15. Collateral
  16. Account liquidity
  17. Open price feed
  18. Enter market and borrow
  19. Borrowed balance (includes interest)
  20. Borrow rate
  21. Repay borrow
  22. Test borrow/repay
  23. Liquidate Contract
  24. Close factor
  25. Liquidation incentive
  26. Liquidate
  27. Test liquidate
  28. Long and Short
  29. Long ETH
  30. Long ETH
  31. Forking mainnet
  32. Note
  33. Roadmap
  34. Contributing
  35. License
  36. Contact
  37. Acknowledgments

About The Project

Compound finance allows you to lend and borrow tokens

(back to top)

Built With

  • Hardhat
  • Ethers

(back to top)

Getting Started

To get a local copy up and running follow these simple example steps.

Prerequisites

  • npm

    npm init -y
  • hardhat

    npm install --save-dev hardhat

    run:

    npx hardhat

    verify:

    npx hardhat verify --network goerli "contract address" "pair address"

Installation

  1. Clone the repo

    git clone https://github.com/Aboudoc/Compound.git
  2. Install NPM packages

    npm install
  3. Dev Dependencies

    npm add --save-dev dotenv

(back to top)

Usage

If you need testnet funds, use the Alchemy testnet faucet.

This project shows how to interact with Compound protocol

You can find a deep overview of Uniswap v3 in this repo

You can learn more about CPAMM in this repo

(back to top)

Supply and Redeem Contract

There are 4 operations related to lending and borrowing on Compound:

  • Supply
  • Borrow
  • Repay
  • Redeem

First let's see how to lend a token to Compound and earn interests

We will start with Supply and Redeem

Let's see how to supply our token to earn interests. Once we'll want to withdraw our token, then we'll be calling redeem.

(back to top)

State Variables

Declare the two token interfaces

(back to top)

Constructor

Set the contract interfaces

(back to top)

Function supply

When we call this function, it will lend our token to the compound protocol

  1. Transfer the token from the caller inside this contract
  2. Approve the cToken to spend the token we transfered before from msg.sender
  3. Call mint() on cToken contract to lend the token to compound
  4. When mint() is called, it returns a number (!0 = there is an error). Check that 0 is returned

The question is: How much cToken do we have?

(back to top)

Function getCTokenBalance

Call balanceOf on cToken

(back to top)

Function getInfo

  1. if you want to know the exchange rate from cToken to the token that we supply, then we can call the function exchangeRate()on cToken
  2. if you want to know the interest rate on supplying the token, then you can get it by calling supplyRatePerBllock() on cToken Note that these two functions are not view functions, this means we need to send a transaction (paying transaction fee) to get these data However we can get these numbers without snding a transaction, by making a static call to these functions

Now we can estimate the balance of the token that we supply

(back to top)

Function estimateBalanceOfUnderlying

  1. Return balance of underlying token using balance of cToken and exchangeRate

You must take into consideration decimals (wbtc = 8 decimals)

We actually don't have to write this function because compound provides the same function called balanceOfUnderlying

(back to top)

Function balanceOfUnderlying

Calculates and returns the amount of token that we supplied into compound.

We'll see that these two function returns numbers really close to each other

(back to top)

Function redeem

To claim the token and the interest

  1. Check that the function call was successfull by calling redeem() on cToken (!0 = there is an error)

(back to top)

Test supply/redeem

Test

supply

  • balance of underlying: We supplied 1 WBTC and the balance is 0.99999 WBTC according to compound

after some blocks

  • balance of underlying: The balance icreased, we gain some interest for lending our WBTC

redeem

  • token balance: We claim the WBTC including the interest rate that we earned

(back to top)

Borrow and Repay Contract

Topics related to this part of the contract:

  • collateral
  • account liquidity - calculate how much can I borrow?
  • open price feed - USD price of token to borrow
  • enter market and borrow
  • borrowed balance (includes interest)
  • borrow rate
  • repay borrow

Start by initializing 2 compound contracts: comptroller and priceFeed

(back to top)

Collateral

After you supply a token to compound, you are able to borrow a percentage of what you supply: collateral factor (for exemple 70%)

Function getCollateralFactor

To get the collateral factor using smart contracts, we need to call markets() on the comptroller passing in the address of the cToken that we supply.

It will return 3 outputs: isListed, colFactor and isComped

Note that the colFactor is scaled to 10**18 => divide it by 1e18 to get in %

(back to top)

Account liquidity

How much can I borrow?

Function getAccountLiquidity

To get the current liquidity of an account, we need to call getAccountLiquidity() on the comptroller passing in the address of this contract

It will return 3 outputs: error, _liquidity and _shortfall

Note that _liquidity: USD amount that we can borrow up to, is scaled up by 10**18

If _shortfall > 0 => subject to liquidation

In Summary:

  • normal circumstance - liquidity > 0 and shortfall == 0
  • liquidity > 0 means account can borrow up to liquidity
  • shortfall > 0 is subject to liquidation, you borrowed over the limit

(back to top)

Open price feed

Why might we need the price in terms of USD?

Because liquidity is in terms of USD, by dividing it by the price of token that we want to borrow, we get the amount of tokens that we can borrow.

Function getPriceFeed

We can get the price of the token that we borrow in terms of USD by calling getunderlyingPrice on the contract priceFeed

(back to top)

Enter market and borrow

What to do to borrow the token:

  1. Enter market
  2. Check liquidity
  3. Calculate max borrow
  4. Borrow 50% of max borrow

Function borrow

  1. Call enterMarkets() on comptroller passing in the tokens that we supply. One cToken here, so we initialize an array with 1 element.
  • Check errors[0]
  1. Call getAccountLiquidity() on comptroller passing in this address.
  • Check error, shortfall and liquidity
  1. Get the price by calling getUnderlyingPrice() on priceFeed passing in the tokens that we borrow
  • Calculate maxBorrow by divinding the liquidity by the price. Scale up liquidity by _decimals
  • Check maxBorrow
  1. Define amount as 50% of maxBorrow

  2. Finally call borrow() on the CErc20 for the _cTokenBorrow, and check while calling the function (0 <=> no error)

(back to top)

Borrowed balance (includes interest)

Once we borrow we can get the balance of the borrowed token including the interest

Function getBorrowedBalance

  • Call borrowBalanceCurrent() on the cToken that we borrowed, passing in this address

Note that this function is not a view function

(back to top)

Borrow rate

Can be interesting to get the borrow rate per block

Function getBorrowRatePerBlock

  • Call borrowRatePerBlock() on the CErc20 for the cToken that we borrowed

(back to top)

Repay borrow

Once we are ready to repay what we've borrowed, we will call the function repay()

Function repay

  • Approve the cToken that we borrowed to be able to spend the token that we borrowed for the _amount
  • Call repayBorrow on CErc20 token at the address of _ctTokenBorrowed passing in the _amount we wish to repay

(back to top)

Test borrow/repay

Test

borrow (before)

  • col factor: The collateral factor is 70%
  • supplied: The amount of amount of WBTC that we supply according to compound is 0.99
  • liquidity: The amount of token that we can borrow in terms of USD is $0 => because we've not entered a market yet
  • price: Price of token we're going to borrow => DAI
  • max borrow: Amount of token of DAI that we can borrow is 0
  • borrowed balance (compound): Amount of DAI that we borrowed is 0

borrow (after)

  • borrowed balalance (erc20): After we borrow, the borrowed balance is 10608.03
  • max borrow: We can further borrow 10618 DAI
  • liquidity: The liquidity in terms of USD is 10626 at this moment

after some blocks

  • borrowed balance (compound): This shows us that the interest rate on borrow is accruing, it incresed

repay

  • borrowed balance (compound): Is 0
  • liquidity: The amount of token that we can borrow increased

(back to top)

Liquidate Contract

Liquidate contract

  • supply
  • borrow max
  • wait few blocks and let borrowed balance > supplied balance * col factor
  • liquidate

Liquidator contract

  • close factor
  • liqidation incentive
  • liquidate

(back to top)

Close factor

The maximum pourcentage of the borrow token that can be repaid

For example an account has borrowed 100 DAI and is subject to liquidation

A close factor of 50% means that we can repay up to 50% of the 100 DAI that was borrowed

(back to top)

Function getCloseFactor

Call closeFactorMantissa() on the comptroller. Divide it buy 10**18 to get it in percentage

(back to top)

Liquidation incentive

When we call liquidate, we pay a portion of the token that was borrowed by another account

In return we are rewarded for portion of the token that was supplied as collateral, and we will receive the collateral at a discount

(back to top)

Function getLiquidationIncentive

Call liquidationIncentiveMantissa() on the comptroller

(back to top)

Function getAmountToBeLiquidated

Call liquidationCalculateSeizeTokens() on the comptroller

This will return error and cTokenCollateralAmount that will be liquidated

Multiply cTokenCollateralAmount by the exchange rate of the collateral cToken, and you will be able to get the amount of collateral that will be liquidated

seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral

seizeTokens = seizeAmount / exchangeRate

= actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)

(back to top)

Liquidate

(back to top)

Function liquidate

  • Transfer the tokenBorrow from msg.sender to this contract for _repayAmount
  • Approve the cTokenBorrow to be able to spend _repayAmount from this contract
  • Finally we cal liquidateBorrow on cTokenBorrow contract passing in the address of the _borrower (undercollaterize), _cTokeenCollateral and the repayAmount. Check that the call is successful (returns 0)

(back to top)

Test liquidate

We are going to supply 1 WBTC then borrow max amount of DAI

(back to top)

Long and Short

Long ETH

We will supply 10 ETH and borrow 600 DAI on Compound (30% of $2000 after shapella, less than the collateral ratio of 70%)

We will sell immediatly our DAI to ETH on Uniswap

After ETH price increases, we will sell on Uniswap ETH to buy back the DAI borrowed on Compound

(back to top)

Short ETH

Supply 10000 DAIto Compound and use it as collateral to borrow1.5 ETH

We will immediately sell this ETH for DAI on Uniswap (sell 1.5 ETH => 3000DAI)

After ETH price decreases, we will buy back 1.5 ETH that we borrowed

(back to top)

Here is what we will do:

  1. supply ETH

  2. borrow stable coin (DAI, USDC)

  3. buy ETH on Uniswap

    when the price of ETH goes up...

  4. sell ETH on Uniswap

  5. repay borrowed stable coin

(back to top)

Test long

Test short

(back to top)

Forking mainnet

hardhat.config.js

  networks: {
        hardhat: {
          forking: {
            url: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_API_KEY}`,
       },
     },
  }

Note: Replace the ${} component of the URL with your personal Alchemy API key.

npx hardhat test test/compoundErc20.test.js

(back to top)

Note

Further reading

You can find a Quick Start guide below

Supplying Assets to the Compound Protocol

Borrowing Assets from the Compound Protocol

You can find official Compound documentation below:

Compound Docs

Exchange Rate

Compound Supply examples

Compound Borrow examples

Sources

Smart Contract Engineer

Compound Developers

(back to top)

Roadmap

  • [-] Main functionalities of the protocol
  • [-] Test on mainnet fork
  • [-] Liquidate
  • [-] Fix test on liquidate
  • [-] Long test
  • Short test
  • README long short test
  • Deploy on mainnet?
  • Further reading

See the open issues for a full list of proposed features (and known issues).

(back to top)

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

(back to top)

License

Distributed under the MIT License. See LICENSE.txt for more information.

(back to top)

Contact

Reda Aboutika - @twitter - [email protected]

Project Link: https://github.com/Aboudoc/Compound.git

(back to top)

Acknowledgments

(back to top)

About

πŸ“Š Lend and borrow tokens on Compound. Liquidate undercollateralized borrowers

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published