# 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}`)
})
```
