[text] LoanManager

Viewer

copydownloadembedprintName: LoanManager
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.17;
  3.  
  4. import "../libraries/LoanLibrary.sol";
  5. import "../libraries/OfferLibrary.sol";
  6.  
  7. import "../interfaces/ILoanManager.sol";
  8.  
  9. import "@openzeppelin/contracts/utils/math/SafeMath.sol";
  10. import "@openzeppelin/contracts/access/Ownable2Step.sol";
  11. import "@openzeppelin/contracts/utils/Counters.sol";
  12.  
  13. contract LoanManager is ILoanManager, Ownable2Step {
  14.     uint256 public constant ONE_DAY = 60 * 60 * 24;
  15.     uint256 public constant ONE_HOUR = 60 * 60;
  16.     uint256 public DUST_AMOUNT = 100;
  17.  
  18.     using Counters for Counters.Counter;
  19.     using SafeMath for uint256;
  20.  
  21.     Counters.Counter private loanIdTracker;
  22.     mapping(uint256 => LoanLibrary.Loan) public loans;
  23.  
  24.     // offerId => wallet addresses
  25.     mapping(uint256 => address[]) private borrowers;
  26.  
  27.     address lendingPool;
  28.     uint16 graceDays = 5;
  29.  
  30.     constructor() Ownable2Step() {}
  31.  
  32.     function createLoan(
  33.         uint256 offerId,
  34.         OfferLibrary.Type offerType,
  35.         address principalToken,
  36.         address collateralToken,
  37.         uint256 principalAmount,
  38.         uint256 collateralAmount,
  39.         uint256 collateralPriceInUSD,
  40.         uint256 interestRate,
  41.         uint16 daysToMaturity,
  42.         uint256 unClaimedBorrowedPrincipal,
  43.         address borrower,
  44.         address lender
  45.     ) public override onlyLendingPool returns (uint256) {
  46.         // avoid 0 id
  47.         loanIdTracker.increment();
  48.         uint256 loanId = loanIdTracker.current();
  49.  
  50.         require(lender != borrower, "ERR_CANT_BORROW_OWN");
  51.  
  52.         if (offerType == OfferLibrary.Type.LENDING_OFFER) {
  53.             require(
  54.                 !_hasBorrowedFrom(offerId, borrower),
  55.                 "ERR_ALREADY_BORROWED"
  56.             );
  57.             borrowers[offerId].push(borrower);
  58.         } else {
  59.             require(!_hasBorrowedFrom(offerId, lender), "ERR_ALREADY_BORROWED");
  60.             borrowers[offerId].push(lender);
  61.         }
  62.  
  63.         uint256 startDate = block.timestamp;
  64.         uint256 duration = ONE_DAY.mul(daysToMaturity);
  65.         uint256 maturityDate = startDate.add(duration);
  66.  
  67.         loans[loanId] = LoanLibrary.Loan(
  68.             offerId,
  69.             LoanLibrary.State.ACTIVE,
  70.             principalToken,
  71.             collateralToken,
  72.             principalAmount,
  73.             principalAmount,
  74.             collateralAmount,
  75.             collateralAmount,
  76.             collateralPriceInUSD,
  77.             interestRate,
  78.             startDate,
  79.             maturityDate,
  80.             graceDays,
  81.             0, // numInstallmentsPaid
  82.             0, // unclaimed principal
  83.             0, // unclaimed collateral
  84.             0, // unclaimed default collateral
  85.             unClaimedBorrowedPrincipal,
  86.             0, // total interestRate paid
  87.             0, // repaidOn
  88.             borrower,
  89.             lender
  90.         );
  91.  
  92.         _emit(loanId, loans[loanId]);
  93.         return loanId;
  94.     }
  95.  
  96.     function getLoan(uint256 loanId)
  97.         public
  98.         view
  99.         override
  100.         returns (LoanLibrary.Loan memory)
  101.     {
  102.         return loans[loanId];
  103.     }
  104.  
  105.     function repayLoan(
  106.         uint256 loanId,
  107.         uint256 interestPaid,
  108.         uint256 principalPaid,
  109.         uint256 collateralRetrieved
  110.     ) public override onlyLendingPool returns (bool) {
  111.         LoanLibrary.Loan storage loan = loans[loanId];
  112.         require(loan.state == LoanLibrary.State.ACTIVE, "ERR_LOAN_NOT_ACTIVE");
  113.  
  114.         loan.numInstallmentsPaid += 1;
  115.         loan.totalInterestPaid += interestPaid;
  116.         loan.unClaimedPrincipal += principalPaid.add(interestPaid);
  117.         loan.unClaimedCollateral += collateralRetrieved;
  118.         loan.currentPrincipal -= principalPaid;
  119.         loan.currentCollateral -= collateralRetrieved;
  120.         loan.repaidOn = block.timestamp;
  121.  
  122.         if (loan.currentPrincipal <= DUST_AMOUNT) {
  123.             loan.state = LoanLibrary.State.REPAID;
  124.         }
  125.  
  126.         _emit(loanId, loan);
  127.         return loan.state == LoanLibrary.State.REPAID;
  128.     }
  129.  
  130.     function claimPrincipal(uint256 loanId, address user)
  131.         public
  132.         override
  133.         onlyLendingPool
  134.         returns (
  135.             uint256,
  136.             uint256,
  137.             address
  138.         )
  139.     {
  140.         LoanLibrary.Loan storage loan = loans[loanId];
  141.         require(loan.lender == user, "ERR_NOT_LENDER");
  142.         require(loan.unClaimedPrincipal > 0, "ERR_ZERO_BALANCE");
  143.         uint256 amount = loan.unClaimedPrincipal;
  144.         loan.unClaimedPrincipal = 0;
  145.  
  146.         _emit(loanId, loan);
  147.         return (amount, loan.offerId, loan.principalToken);
  148.     }
  149.  
  150.     function claimDefaultCollateral(uint256 loanId, address user)
  151.         public
  152.         override
  153.         onlyLendingPool
  154.         returns (
  155.             uint256,
  156.             uint256,
  157.             address
  158.         )
  159.     {
  160.         LoanLibrary.Loan storage loan = loans[loanId];
  161.         require(loan.lender == user, "ERR_NOT_LENDER");
  162.         require(loan.unClaimedDefaultCollateral > 0, "ERR_ZERO_BALANCE");
  163.         uint256 amount = loan.unClaimedDefaultCollateral;
  164.         loan.unClaimedDefaultCollateral = 0;
  165.  
  166.         _emit(loanId, loan);
  167.         return (amount, loan.offerId, loan.collateralToken);
  168.     }
  169.  
  170.     function claimCollateral(uint256 loanId, address user)
  171.         public
  172.         override
  173.         onlyLendingPool
  174.         returns (
  175.             uint256,
  176.             uint256,
  177.             address
  178.         )
  179.     {
  180.         LoanLibrary.Loan storage loan = loans[loanId];
  181.         require(loan.borrower == user, "ERR_NOT_BORROWER");
  182.         require(loan.unClaimedCollateral > 0, "ERR_ZERO_BALANCE");
  183.         uint256 amount = loan.unClaimedCollateral;
  184.         loan.unClaimedCollateral = 0;
  185.  
  186.         _emit(loanId, loan);
  187.         return (amount, loan.offerId, loan.collateralToken);
  188.     }
  189.  
  190.     function claimBorrowedPrincipal(uint256 loanId, address user)
  191.         public
  192.         override
  193.         onlyLendingPool
  194.         returns (
  195.             uint256,
  196.             uint256,
  197.             address
  198.         )
  199.     {
  200.         LoanLibrary.Loan storage loan = loans[loanId];
  201.         require(loan.borrower == user, "ERR_NOT_BORROWER");
  202.         require(loan.unClaimedBorrowedPrincipal > 0, "ERR_ZERO_BALANCE");
  203.         uint256 amount = loan.unClaimedBorrowedPrincipal;
  204.         loan.unClaimedBorrowedPrincipal = 0;
  205.  
  206.         _emit(loanId, loan);
  207.         return (amount, loan.offerId, loan.principalToken);
  208.     }
  209.  
  210.     function liquidateLoan(
  211.         uint256 loanId,
  212.         uint256 principalPaid,
  213.         uint256 collateralRetrieved,
  214.         uint256 collateralPaid
  215.     ) public override onlyLendingPool {
  216.         LoanLibrary.Loan storage loan = loans[loanId];
  217.         require(loan.state == LoanLibrary.State.ACTIVE, "ERR_LOAN_NOT_ACTIVE");
  218.  
  219.         uint256 graceDate = ONE_DAY.mul(loan.graceDays);
  220.         uint256 defaultDate = loan.maturityDate.add(graceDate);
  221.         uint256 currentDate = block.timestamp;
  222.  
  223.         require(defaultDate >= currentDate, "ERR_LOAN_NOT_MATURED");
  224.  
  225.         loan.currentPrincipal -= principalPaid;
  226.         loan.currentCollateral -= collateralRetrieved;
  227.         loan.unClaimedDefaultCollateral += collateralPaid;
  228.  
  229.         if (loan.currentCollateral > 0) {
  230.             loan.unClaimedCollateral = loan.currentCollateral;
  231.             loan.currentCollateral = 0;
  232.         }
  233.  
  234.         if (loan.currentPrincipal <= 10) {
  235.             loan.state = LoanLibrary.State.REPAID_DEFAULTED;
  236.         } else {
  237.             loan.state = LoanLibrary.State.ACTIVE_DEFAULTED;
  238.         }
  239.  
  240.         _emit(loanId, loan);
  241.     }
  242.  
  243.     function _emit(uint256 loanId, LoanLibrary.Loan memory loan) private {
  244.         emit LoanLibrary.LoanCreated(
  245.             loanId,
  246.             loan.offerId,
  247.             loan.state,
  248.             loan.principalToken,
  249.             loan.collateralToken,
  250.             loan.initialPrincipal,
  251.             loan.currentPrincipal,
  252.             loan.initialCollateral,
  253.             loan.currentCollateral,
  254.             loan.interestRate,
  255.             loan.startDate,
  256.             loan.maturityDate,
  257.             loan.graceDays,
  258.             loan.borrower,
  259.             loan.lender
  260.         );
  261.         emit LoanLibrary.LoanCreatedProperty(
  262.             loanId,
  263.             loan.collateralPriceInUSD,
  264.             loan.numInstallmentsPaid,
  265.             loan.unClaimedPrincipal,
  266.             loan.unClaimedCollateral,
  267.             loan.unClaimedDefaultCollateral,
  268.             loan.unClaimedBorrowedPrincipal,
  269.             loan.totalInterestPaid,
  270.             loan.repaidOn
  271.         );
  272.     }
  273.  
  274.     function _hasBorrowedFrom(uint256 offerId, address user)
  275.         private
  276.         view
  277.         returns (bool)
  278.     {
  279.         for (uint16 index = 0; index < borrowers[offerId].length; index++) {
  280.             if (borrowers[offerId][index] == user) {
  281.                 return true;
  282.             }
  283.         }
  284.         return false;
  285.     }
  286.  
  287.     function setGraceDays(uint16 days_) public onlyOwner {
  288.         graceDays = days_;
  289.     }
  290.  
  291.     function setLendingPool(address address_) public onlyOwner {
  292.         lendingPool = address_;
  293.     }
  294.  
  295.     modifier onlyLendingPool() {
  296.         require(msg.sender == lendingPool);
  297.         _;
  298.     }
  299. }
  300.  

Editor

You can edit this paste and save as new:


File Description
  • LoanManager
  • Paste Code
  • 15 Aug-2023
  • 9.09 Kb
You can Share it: