# Borrow and Repay

## Intro

{% embed url="<https://youtu.be/d9_ZEvLDSl8>" %}
Borrow and Repay
{% endembed %}

In this section we are going to implement the borrower's functions `borrow` and `repay`. The following concepts will be covered:

* 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

Code:

{% embed url="<https://github.com/stakewithus/defi-by-example/blob/main/contracts/TestCompoundErc20.sol>" %}
TestCompoundErc20.sol
{% endembed %}

## Setup

Initialize Compound controller and price feed:

```solidity
// borrow and repay //
Comptroller public comptroller = Comptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);
PriceFeed public priceFeed = PriceFeed(0x922018674c12a7F0D394ebEEf9B58F186CdE13c1);
```

## Collateral

Query collateral factor via `comptroller.markets()`:

```solidity
// collateral
function getCollateralFactor() external view returns (uint) {
    (bool isListed, uint colFactor, bool isComped) = comptroller.markets(
        address(cToken)
    );
    return colFactor; // divide by 1e18 to get in %
}
```

## Account Liquidity

How much can I borrow? We can query the maximal amount we can borrow via `comptroller.getAccountLiquidity()`:

```solidity
// account liquidity - calculate how much can I borrow?
// sum of (supplied balance of market entered * col factor) - borrowed
function getAccountLiquidity()
    external
    view
    returns (uint liquidity, uint shortfall)
{
    // liquidity and shortfall in USD scaled up by 1e18
    (uint error, uint _liquidity, uint _shortfall) = comptroller.getAccountLiquidity(
        address(this)
    );
    // error == 0 means no error
    require(error == 0, "error");
    // 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 limit
    return (_liquidity, _shortfall);
}
```

`shortfall > 0` means borrowing amount exceeds limit and the collateral is facing liquidation. Under normal circumstances we want `liquidity > 0` and `shortfall == 0`.

`_liquidity` is in USD.

## Price Feed

USD price for borrowing token can be queried via `priceFeed.getUnderlyingPrice()`:

```solidity
// open price feed - USD price of token to borrow
function getPriceFeed(address _cToken) external view returns (uint) {
    // scaled up by 1e18
    return priceFeed.getUnderlyingPrice(_cToken);
}
```

We need this function to compute how many cTokens we can borrow, since `_liquidity` divided by price gives us the amount of cTokens we can borrow.

## borrow() - borrower enters market and borrows loan

We are going to build the `borrow()` function on top of the helper functions we just wrote. Here is the plan:

* Step 1: enter market
* Step 2: check account liquidity (how much we can borrow in USD)
* Step 3: calculate max amount of cTokens that we can borrow
* Step 4: borrow 50% of max borrow

**Step 1: enter market**

```solidity
// enter market
// enter the supply market so you can borrow another type of asset
address[] memory cTokens = new address[](1);
cTokens[0] = address(cToken);
uint[] memory errors = comptroller.enterMarkets(cTokens);
require(errors[0] == 0, "Comptroller.enterMarkets failed.");
```

**Step 2: check account liquidity (how much we can borrow in USD)**

```solidity
// check liquidity
(uint error, uint liquidity, uint shortfall) = comptroller.getAccountLiquidity(
    address(this)
);
require(error == 0, "error");
require(shortfall == 0, "shortfall > 0");
require(liquidity > 0, "liquidity = 0");
```

**Step 3: calculate max amount of cTokens that we can borrow**

```solidity
// calculate max borrow
uint price = priceFeed.getUnderlyingPrice(_cTokenToBorrow);
    
// liquidity - USD scaled up by 1e18
// price - USD scaled up by 1e18
// decimals - decimals of token to borrow
uint maxBorrow = (liquidity * (10**_decimals)) / price;
require(maxBorrow > 0, "max borrow = 0");
```

**Step 4: borrow 50% of max borrow**

```solidity
// borrow 50% of max borrow
uint amount = (maxBorrow * 50) / 100;
require(CErc20(_cTokenToBorrow).borrow(amount) == 0, "borrow failed");
```

Here is the complete implementation of `borrow()`:

```solidity
// enter market and borrow
function borrow(address _cTokenToBorrow, uint _decimals) external {
    // enter market
    // enter the supply market so you can borrow another type of asset
    address[] memory cTokens = new address[](1);
    cTokens[0] = address(cToken);
    uint[] memory errors = comptroller.enterMarkets(cTokens);
    require(errors[0] == 0, "Comptroller.enterMarkets failed.");

    // check liquidity
    (uint error, uint liquidity, uint shortfall) = comptroller.getAccountLiquidity(
        address(this)
    );
    require(error == 0, "error");
    require(shortfall == 0, "shortfall > 0");
    require(liquidity > 0, "liquidity = 0");

    // calculate max borrow
    uint price = priceFeed.getUnderlyingPrice(_cTokenToBorrow);

    // liquidity - USD scaled up by 1e18
    // price - USD scaled up by 1e18
    // decimals - decimals of token to borrow
    uint maxBorrow = (liquidity * (10**_decimals)) / price;
    require(maxBorrow > 0, "max borrow = 0");

    // borrow 50% of max borrow
    uint amount = (maxBorrow * 50) / 100;
    require(CErc20(_cTokenToBorrow).borrow(amount) == 0, "borrow failed");
}
```

Two utility functions related to borrow:

```solidity
// borrowed balance (includes interest)
// not view function
function getBorrowedBalance(address _cTokenBorrowed) public returns (uint) {
    return CErc20(_cTokenBorrowed).borrowBalanceCurrent(address(this));
}

// borrow rate
function getBorrowRatePerBlock(address _cTokenBorrowed) external view returns (uint) {
    // scaled up by 1e18
    return CErc20(_cTokenBorrowed).borrowRatePerBlock();
 }

```

## repay() - borrower repays loan

Repay the borrowed cTokens via `CErc20.repayBorrow()`:

```solidity
// repay borrow
function repay(
    address _tokenBorrowed,
    address _cTokenBorrowed,
    uint _amount
) external {
    IERC20(_tokenBorrowed).approve(_cTokenBorrowed, _amount);
    // _amount = 2 ** 256 - 1 means repay all
    require(CErc20(_cTokenBorrowed).repayBorrow(_amount) == 0, "repay failed");
}
```

## Test

{% embed url="<https://github.com/stakewithus/defi-by-example/blob/main/test/test-compound-erc20-borrow.js>" %}
test-compound-erc20-borrow\.js
{% endembed %}

Test case:

```javascript
it("should supply, borrow and repay", async () => {
    // used for debugging
    let tx
    let snap

    // supply
    await token.approve(testCompound.address, SUPPLY_AMOUNT, {
        from: WHALE
    })
    tx = await testCompound.supply(SUPPLY_AMOUNT, {
        from: WHALE,
    })

    // borrow
    snap = await snapshot(testCompound, tokenToBorrow)
    console.log(`--- borrow (before) ---`)
    console.log(`col factor: ${snap.colFactor} %`)
    console.log(`supplied: ${snap.supplied}`)
    console.log(`liquidity: $ ${snap.liquidity}`)
    console.log(`price: $ ${snap.price}`)
    console.log(`max borrow: ${snap.maxBorrow}`)
    console.log(`borrowed balance (compound): ${snap.borrowedBalance}`)
    console.log(`borrowed balance (erc20): ${snap.tokenToBorrowBal}`)
    console.log(`borrow rate: ${snap.borrowRate}`)

    tx = await testCompound.borrow(C_TOKEN_TO_BORROW, BORROW_DECIMALS, {
        from: WHALE
    })
    // for (const log of tx.logs) {
    //   console.log(log.event, log.args.message, log.args.val.toString())
    // }

    snap = await snapshot(testCompound, tokenToBorrow)
    console.log(`--- borrow (after) ---`)
    console.log(`liquidity: $ ${snap.liquidity}`)
    console.log(`max borrow: ${snap.maxBorrow}`)
    console.log(`borrowed balance (compound): ${snap.borrowedBalance}`)
    console.log(`borrowed balance (erc20): ${snap.tokenToBorrowBal}`)

    // accrue interest on borrow
    const block = await web3.eth.getBlockNumber()
    await time.advanceBlockTo(block + 100)

    snap = await snapshot(testCompound, tokenToBorrow)
    console.log(`--- after some blocks... ---`)
    console.log(`liquidity: $ ${snap.liquidity}`)
    console.log(`max borrow: ${snap.maxBorrow}`)
    console.log(`borrowed balance (compound): ${snap.borrowedBalance}`)
    console.log(`borrowed balance (erc20): ${snap.tokenToBorrowBal}`)

    // repay
    await tokenToBorrow.transfer(testCompound.address, BORROW_INTEREST, {
        from: REPAY_WHALE
    })
    const MAX_UINT = pow(2, 256).sub(new BN(1))
    tx = await testCompound.repay(TOKEN_TO_BORROW, C_TOKEN_TO_BORROW, MAX_UINT, {
        from: REPAY_WHALE,
    })

    snap = await snapshot(testCompound, tokenToBorrow)
    console.log(`--- repay ---`)
    console.log(`liquidity: $ ${snap.liquidity}`)
    console.log(`max borrow: ${snap.maxBorrow}`)
    console.log(`borrowed balance (compound): ${snap.borrowedBalance}`)
    console.log(`borrowed balance (erc20): ${snap.tokenToBorrowBal}`)
})
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ret2basic.gitbook.io/ctfnote/web3-security-research/defi/compound-v3/interacting-with-compound/borrow-and-repay.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
