Borrowing Protocol

As explained above, a reserve of USDC is maintained and the users holding clETH can take a loan against clETH as collateral.

Fetch clETH price

function fetchCLETHPrice() public view whenNotPaused returns (uint256) {
       (
           int price,
           uint256 timeStamp,
       ) = priceFeed.latestRoundData();
       // Ensure the price is not zero
       require(price > 0, "CLETH: fetched price is zero");
       // Check if the price data is recent enough
       require(block.timestamp - timeStamp < 10 minutes, "Price data is too old");
       return uint256(price);
   }
  1. The clETH price is fetched from the Oracle without updating the contract state.

  2. There are checks put up for pause, price zero and price data timestamps.

Calculate LTV

function calculateMaxLTV() public view returns (uint256) {
       uint256 baseLTV = 70;
       uint256 currentUtilization = ((totalLoans / 1e18) * 100) /
           (totalfund / 1e18);
       if (currentUtilization < ltvTargetUtilization) {
           return baseLTV;
       }
       uint256 ltvAdjustment = ltvK *
           (currentUtilization - ltvTargetUtilization);
       return baseLTV > ltvAdjustment ? baseLTV - ltvAdjustment : 0;
}
  1. Base LTV is set to 70 by default.

  2. If the USDC reserve utilization is less than the target utilization, the LTV will remain the same.

  3. Otherwise, the LTV is increased against the LTV constant at 1.

Calculate Interest Rate

function calculateInterestRate() public view returns (uint256 interestRate)
{
   uint256 baseRate = 6;
   uint256 currentUtilization = (totalLoans * 100) /
       (totalfund > 0 ? totalfund : 1);
   interestRate = baseRate + interestRateK *
       (
           currentUtilization > interestRateTargetUtilization
               ? currentUtilization - interestRateTargetUtilization
               : 0
       );
   return interestRate * 1e18;
}
  1. Base Interest Rate is set to 6% by default.

  2. Depending on the USDC reserve utilization, the Interest rate is increased by the interest rate constant multiple difference between current and target utilization.

  3. This interest rate constant is set to 1 by default.

Calculate Max Loan Amount

function calculateMaxLoanAmount(
       uint256 clethAmount
   ) public view ZeroAmount(clethAmount) returns (uint256) {
       uint256 collateralValue = clethAmount * fetchCLETHPrice();
       uint256 maxLTV = calculateMaxLTV();
       return (collateralValue * maxLTV) / 100000000000000000000;
}
  1. The value of the collateral is always higher than the amount of the Loan.

  2. The Max Loan Amount depends on the max LTV of the smart contract.

Create Loan

function createLoan(
       uint256 clethAmount
   ) public ZeroAmount(clethAmount) nonReentrant {
       uint256 maxLoanAmount = calculateMaxLoanAmount(clethAmount);
       require(maxLoanAmount <= totalfund, "Insufficient liquidity");
       transferTokensFrom(clethToken, msg.sender, clethAmount);
       uint256 interestRate = calculateInterestRate();
       .
       //store loan details 
       .
       .
       transferTokens(usdcToken, msg.sender, maxLoanAmount);
       totalUSDCReserve -= maxLoanAmount;
       totalLoans += maxLoanAmount;
       .
       .
       nextLoanId++;
}
  1. Max Loan that can be taken is directly dependent on the current LTV and the clETH price.

  2. A new loan account is created once the clETH is transferred from the user wallet to the smart contract.

  3. Depending on the max loan amount calculation, USDC is transferred to the user’s wallet.

Repay Loan

function repayLoan(
       uint256 loanId
   ) public LoanIdNotExits(loanId) nonReentrant {
       Loan storage loan = loans[loanId];
       require(!loan.isRepaid, "Loan already repaid");
       require(
           loan.borrower == msg.sender,
           "Only the borrower can repay the loan"
       );
       uint256 repaymentAmount = calculateRepaymentAmount(loanId);
       transferTokensFrom(usdcToken, msg.sender, repaymentAmount);
       loan.isRepaid = true;
       totalLoans -= loan.amount;
       totalUSDCReserve += repaymentAmount;
       transferTokens(clethToken, loan.borrower, loan.collateralAmount);      
}
  1. A loan is expected to be paid in full, so the total repayment amount is calculated when the user wants to repay the loan.

  2. Once the user replays USDC equivalent to the repayment amount, the clETH is transferred to the user’s wallet, and the loan is closed.

Liquidate collateral

function liquidateCollateral(
       uint256 loanId
   ) public LoanIdNotExits(loanId) nonReentrant whenNotPaused {
       Loan storage loan = loans[loanId];
       require(!loan.isRepaid, "Loan is already repaid or liquidated");
       uint256 currentPrice = fetchCLETHPrice();
       uint256 loanValueInCLETH = loan.debt / currentPrice;
       uint256 currentLTV = calculateMaxLoanAmount(loanValueInCLETH);
       require(currentLTV > liquidationThreshold, "Loan LTV is below liquidation threshold");
        .
        .
       require(clethToken.transfer(recoveryAddress, loan.collateralAmount), "Transfer failed");
       loan.isRepaid = true;
       loan.debt = 0;
       totalLoans -= loan.amount;
        .
}

function setRecoveryAddress(address _recoveryAddress) public onlyOwner {
       require(_recoveryAddress != address(0), "Invalid address");
       recoveryAddress = _recoveryAddress;
}
  1. If the current LTV of the loan amount is greater than the liquidity threshold of 80%, the collateral is liquidated.

  2. The loan is marked as repaid.

Modifiers

modifier ZeroAmount(uint256 amount) {
       require(amount > 0, "Amount cannot be zero");
       _;
}

modifier ZeroAddress(address account) {
       require(account != address(0), "Address cannot be zero");
       _;
}
  1. ZeroAmount is used to check if the amount is greater than 0.

  2. Zeroaddress is used to check if the passed address is zero value.

Last updated