Project #5: Collateralized Vault

Objective

  • Contract that acts as a collateralized debt engine.

  • Contract allows users to deposit an asset they own (collateral), to borrow a different asset that the Vault owns (underlying).

  • Exchange rate determined by oracle. If value of collateral drops in underlying terms, the user will be liquidated.

Tokens

  • Collateral: WETH

  • Underlying: DAI

Contract

  1. Pull the contract code for both from Etherscan

  2. Add a mint() function that allows you to obtain as much as you need for testing.

Workflow

  1. Deposit: User deposit WETH into Vault. Vault records WETH deposited.

  2. Borrow: User borrows DAI against their WETH deposit.

    • As long as the value DAI they borrow in WETH terms is less than the value of their WETH collateral

    • DAI_value_in_WETH < WETH deposit

    • Vault transfers DAI to the users

    • Vault owner finances DAI to the Vault on deployment

  3. Exchange rate: Chainlink Oracle [https://docs.chain.link/docs/ethereum-addresses]

  4. Withdrawal

    • To withdraw WETH, the users must repay the DAI they borrowed.

  5. Liquidation

    • If ETH/DAI price changes such tt debt_value > collateral_value, Vault will erase user records from the contract -> cancelling the user debt, and at the same time stopping that user from withdrawing their collateral.

Process

Dependencies

  • IERC20 interface): forge install yieldprotocol/yield-utils-v2

  • Ownable.sol: forge install openZeppelin/openzeppelin-contracts

  • AggregatorV3 and MockV3Aggregator: forge install smartcontractkit/chainlink

Pull contracts from Etherscan

Updating contracts to v0.8.0

WETH9.sol:

  1. Added emit to all events

  2. Line 70: uint(-1) refactored to type(uint).max

DAI.sol:

  • removed inheritance of LibNote

  • line 78 & 79: removed note modifier from function reply and deny

  • line 112: removed public from constructor.

  • line 190: now deprecated, changed to block.timestamp

  • line 192: uint(-1) changed to type(uint).max (also on 131, 147)

Pricing + Decimal scaling

Testing

Action: deposit, borrow, repay, withdraw. liquidated

StateZero:(Vault has 10000 DAI)

  • testVaultHasDAI

  • User deposits WETH

  • cannotWithdraw -> nothing to withdraw. no need to test.

StateDeposited: (Vault has 10000 DAI, 1 WETH) | (user deposited 1 WETH. can borrow, withdraw)

  • userCannotWithdrawExcess

  • userCannotBeLiquidatedWithoutDebt

  • userCannotBorrowInExcess

  • User can withdraw freely in absence of debt (fuzzing)

  • User can borrow against collateral provided

StateBorrowed: (user has borrowed half of maxDebt. actions:[borrow,repay,withdraw])

  • userCannotBorrowExcessOfMargin

  • userCannotWithdrawExcessOfMargin

  • userCannotbeLiquidated - price unchanged.

  • User Repays (Fuzzing)

StateLiquidation (setup price to exceed)

  • userLiquidated

  • testOnlyOwnerCanCallLiquidate

Last updated