# Supply and Redeem

## Intro

{% embed url="https://youtu.be/dHKLcbqFzvE" %}
Supply and Redeem
{% endembed %}

`TestCompoundErc20.sol` provides the following 4 functionalities:

* `supply()` (lender deposits collateral)
* `redeem()` (lender withdraws collateral)
* `borrow()` (borrower enters market and borrows loan)
* `repay()` (borrower pays back loan)

In this section we are going to implement the lender's functions `supply` and `redeem`. The sample code is here:

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

## Setup

In the constructor, we initialize the ERC20 token that we wish to use as collateral and cToken contract address:

<pre class="language-solidity"><code class="lang-solidity">// SPDX-License-Identifier: MIT
pragma solidity ^0.8;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/compound.sol";

// supply()
// redeem()
// borrow()
// repay()

<strong>contract TestCompoundErc20 {
</strong>    IERC20 public token;
    CErc20 public cToken;

    constructor(address _token, address _cToken) {
        token = IERC20(_token);
        cToken = CErc20(_cToken);
    }
}
</code></pre>

## supply() - lender deposits collateral

Lender calls `supply(uint _amount)` to deposit ERC20 token as collateral and get cToken back as "receipt token":

```solidity
function supply(uint _amount) external {
    // Step 1: transfer ERC20 token from lender's wallet to this contract
    token.transferFrom(msg.sender, address(this), _amount);

    // Step 2: approve cToken contract to spend the token we just transfered
    token.approve(address(cToken), _amount);

    // Step 3: mint cToken, equivalent to deposit collateral to Compound
    // cToken.mint() returning 0 means function call succeeded
    require(cToken.mint(_amount) == 0, "mint failed");
}
```

When calling `cToken.mint()`, the ERC20 token in this contract will be transferred to the `cToken` contract via `transferFrom()`. This is why we have to do `token.approve()` first.

## redeem() - lender withdraws collateral

This function is the opposite of `supply()`. Lender calls `redeem()` to burn cToken and get ERC20 token back (interest is included in cToken price):

```solidity
function redeem(uint _cTokenAmount) external {
    // cToken.redeem() returning 0 means function call succeeded
    require(cToken.redeem(_cTokenAmount) == 0, "redeem failed");
}
```

This is just a wrapper that calls `cToken.redeem()`.

## Utility Functions

We need a getter to query cToken balance:

```solidity
function getCTokenBalance() external view returns (uint) {
    return cToken.balanceOf(address(this));
}
```

Query exchange rate and supply rate:

```solidity
// not view function
function getInfo() external returns (uint exchangeRate, uint supplyRate) {
    // Exchange rate between cToken and underlying token
    // For example, cETH <-> ETH begins at 0.02 and is increased by APR
    exchangeRate = cToken.exchangeRateCurrent();
    
    // Amount added to you supply balance this block
    // This is just the interest rate
    supplyRate = cToken.supplyRatePerBlock();
}
```

We can estimate underlying asset balance by the formula `cTokenBal * exchangeRate`. And figure out some messy decimals things:

```solidity
// not view function
function estimateBalanceOfUnderlying() external returns (uint) {
    uint cTokenBal = cToken.balanceOf(address(this));
    uint exchangeRate = cToken.exchangeRateCurrent();
    uint decimals = 8; // WBTC = 8 decimals
    uint cTokenDecimals = 8;

    return (cTokenBal * exchangeRate) / 10**(18 + decimals - cTokenDecimals);
}
```

Official API for querying underlying asset balance:

```solidity
// not view function
function balanceOfUnderlying() external returns (uint) {
    // cToken.balanceOfUnderlying() returning 0 means function call succeeded
    return cToken.balanceOfUnderlying(address(this));
 }
```

## Tests

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

In the setup, we create a whale user with each WBTC to run this demo:

```javascript
beforeEach(async () => {
    await sendEther(web3, accounts[0], WHALE, 1)

    testCompound = await TestCompoundErc20.new(TOKEN, C_TOKEN)
    token = await IERC20.at(TOKEN)
    cToken = await CErc20.at(C_TOKEN)

    const bal = await token.balanceOf(WHALE)
    console.log(`whale balance: ${bal}`)
    assert(bal.gte(DEPOSIT_AMOUNT), "bal < deposit")
 })
```

Create a function `snapshot()` that logs current states:

```javascript
const snapshot = async (testCompound, token, cToken) => {
    const { exchangeRate, supplyRate } = await testCompound.getInfo.call()

    return {
        exchangeRate,
        supplyRate,
        estimateBalance: await testCompound.estimateBalanceOfUnderlying.call(),
        balanceOfUnderlying: await testCompound.balanceOfUnderlying.call(),
        token: await token.balanceOf(testCompound.address),
        cToken: await cToken.balanceOf(testCompound.address),
    }
}
```

Test case:

```javascript
it("should supply and redeem", async () => {
    await token.approve(testCompound.address, DEPOSIT_AMOUNT, {
        from: WHALE
    })

    let tx = await testCompound.supply(DEPOSIT_AMOUNT, {
        from: WHALE,
    })

    let after = await snapshot(testCompound, token, cToken)

    // for (const log of tx.logs) {
    //   console.log(log.event, log.args.message, log.args.val.toString())
    // }

    console.log("--- supply ---")
    console.log(`exchange rate ${after.exchangeRate}`)
    console.log(`supply rate ${after.supplyRate}`)
    console.log(`estimate balance ${after.estimateBalance}`)
    console.log(`balance of underlying ${after.balanceOfUnderlying}`)
    console.log(`token balance ${after.token}`)
    console.log(`c token balance ${after.cToken}`)

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

    after = await snapshot(testCompound, token, cToken)

    console.log(`--- after some blocks... ---`)
    console.log(`balance of underlying ${after.balanceOfUnderlying}`)

    // test redeem
    const cTokenAmount = await cToken.balanceOf(testCompound.address)
    tx = await testCompound.redeem(cTokenAmount, {
        from: WHALE,
    })

    after = await snapshot(testCompound, token, cToken)

    console.log(`--- redeem ---`)
    console.log(`balance of underlying ${after.balanceOfUnderlying}`)
    console.log(`token balance ${after.token}`)
    console.log(`c token balance ${after.cToken}`)
})
```
