1

I am trying to create a Credist Score Function in a Loan Management Smart Contract using solidity, but I keep getting this undeclared identifier error when I compile on remix browser IDE and truffle. How can I solve this problem please?

I have attached my code here:

pragma solidity ^0.6.0; pragma experimental ABIEncoderV2;

import "../IERC20.sol"; import "./Loan.sol";

interface TrustTokenInterface { function isTrustee(address) external view returns (bool);

function balanceOf(address) external view returns (uint256);

}

interface ProposalManagementInterface { function memberId(address) external view returns (uint256);

function contractFee() external view returns (uint256);

function setLoanManagement(address) external;

function transferTokensFrom(address, address, uint256) external returns (bool);

}

interface LoanInterface {

struct LoanParams {
    address lender;
    address borrower;
    bool initiatorVerified;
    uint256 principalAmount;
    uint256 paybackAmount;
    uint256 contractFee;
    string purpose;
    address collateralToken;
    uint256 collateralAmount;
    uint256 duration;
    uint256 effectiveDate;
}

function managementAcceptLoanOffer(address) external;

function managementAcceptLoanRequest(address) external;

function managementReturnCollateral() external;

function managementDefaultOnLoan() external;

function cleanUp() external;

function borrower() external view returns (address);

function lender() external view returns (address);

function getLoanParameters() external view returns (LoanParams memory);

function getLoanStatus() external view returns (uint8);

function refreshAndGetLoanStatus() external returns (uint8);

}

contract LoanManagement {

// Loan platform settings.
address payable private trustToken;
address private proposalManagement;

// Loan management variables.
mapping(address => uint256) private userRequestCount;
mapping(address => uint256) private userOfferCount;
mapping(address => bool) private validLoanAd;
mapping(address => bool) private openLoan;
address[] private loanRequests;
address[] private loanOffers;

// Credit rating system variables.
mapping(address => uint256) public borrowerRatings;
mapping(address => uint256) public lenderRatings;

// Event for when a borrower requests a loan.
event LoanRequested();
// Event for when a lender offers a loan.
event LoanOffered();
// Event for when a borrower accepts a loan offer, or a lender accepts a loan request.
event LoanGranted();
// Event for a borrower deposits collateral to the loan.
// Event for when a borrower withdraws the loan's value.
event LoanDisbursed();
// Event for when a borrower repays or a lender withdraws collateral.
event LoanSettled();

/**
 * @notice Creates an instance of the LoanManagement contract.
 * @param _trustToken Address of the TrustToken
 * @param _proposalManagement Address of the ProposalManagement
 */
constructor(
    address payable _trustToken,
    address _proposalManagement) public {
    trustToken = _trustToken;
    proposalManagement = _proposalManagement;
    ProposalManagementInterface(proposalManagement).setLoanManagement(address(this));
}

/**
 * @notice Creates a request from a borrower for a new loan.
 * @param _principalAmount Loan principal amount in Wei
 * @param _paybackAmount Loan repayment amount (in Wei?)
 * @param _purpose Purpose(s) the loan will be used for
 * @param _collateralToken Address of the token to be used for collateral
 * @param _collateralAmount Amount of collateral (denominated in _collateralToken) required
 * @param _duration Length of time borrower has to repay the loan from when the lender deposits the principal
 */
function createLoanRequest(
    uint256 _principalAmount,
    uint256 _paybackAmount,
    string memory _purpose,
    address _collateralToken,
    uint256 _collateralAmount,
    uint256 _duration) public {

    // Validate the input parameters.
    require(_principalAmount > 0, "Principal amount must be greater than 0");
    require(_paybackAmount > _principalAmount, "Payback amount must be greater than principal");
    require(userRequestCount[msg.sender] < 5, "Too many loan requests made");
    require(_collateralToken == address(trustToken), "Only BBET is currently supported as a collateral token");
    require(_duration >= 60 * 60 * 12, "Loan duration must be at least 12 hours");

    // Check if borrower is a verified member.
    bool borrowerVerified = TrustTokenInterface(address(trustToken)).isTrustee(msg.sender);
    borrowerVerified = borrowerVerified || ProposalManagementInterface(proposalManagement).memberId(msg.sender) != 0;
    require(borrowerVerified, "Must be a DFND holder to request loans");

    // Get contract fee.
    uint256 contractFee = ProposalManagementInterface(proposalManagement).contractFee();

    // Check if the borrower has enough collateral.
    require(IERC20(_collateralToken).balanceOf(msg.sender) > _collateralAmount, "Insufficient collateral in account");

    // Create new Loan contract.
    address loan = address(
        new Loan(
            payable(proposalManagement), trustToken, address(0), msg.sender,
            _principalAmount, _paybackAmount, contractFee, _purpose,
            _collateralToken, _collateralAmount, _duration
        )
    );

    // Update number of active requests by the borrower.
    userRequestCount[msg.sender]++;

    // Add new loan request to management structures.
    loanRequests.push(loan);

    // Mark requested loan as a valid ad (request/offer).
    validLoanAd[loan] = true;

    // Trigger LoanRequested event.
    // TODO emit LoanRequested();

    // TODO In web3.js: Ask user to approve spending of collateral by management contract.
}

/**
 * @notice Creates an offer by a lender for a new loan.
 * @param _principalAmount Loan principal amount in Wei
 * @param _paybackAmount Loan repayment amount (in Wei?)
 * @param _purpose Purpose(s) the loan will be used for
 * @param _collateralToken Address of the token to be used for collateral
 * @param _collateralAmount Amount of collateral (denominated in _collateralToken) required
 * @param _duration Length of time borrower has to repay the loan from when the lender deposits the principal
 */
function createLoanOffer(
    uint256 _principalAmount,
    uint256 _paybackAmount,
    string memory _purpose,
    address _collateralToken,
    uint256 _collateralAmount,
    uint256 _duration) public {

    // Validate the input parameters.
    require(_principalAmount > 0, "Principal amount must be greater than 0");
    require(_paybackAmount > _principalAmount, "Payback amount must be greater than principal");
    require(userOfferCount[msg.sender] < 5, "Too many loan offers made");
    require(_collateralToken == address(trustToken), "Only BBET is currently supported as a collateral token");
    require(_duration >= 60 * 60 * 12, "Loan duration must be at least 12 hours");

    // Check if lender is a verified member.
    bool lenderVerified = TrustTokenInterface(address(trustToken)).isTrustee(msg.sender);
    lenderVerified = lenderVerified || ProposalManagementInterface(proposalManagement).memberId(msg.sender) != 0;
    require(lenderVerified, "Must be a DFND holder to offer loans");

    // Get contract fee.
    uint256 contractFee = ProposalManagementInterface(proposalManagement).contractFee();

    // Make sure the lender has enough DFND to pay the principal.
    require(IERC20(trustToken).balanceOf(msg.sender) > _principalAmount, "Insufficient balance to offer this loan");

    // Create new Loan contract.
    address loan = address(
        new Loan(
            payable(proposalManagement), trustToken, msg.sender, address(0),
            _principalAmount, _paybackAmount, contractFee, _purpose,
            _collateralToken, _collateralAmount, _duration
        )
    );

    // Update number of offers made by the lender.
    userOfferCount[msg.sender]++;

    // Add new loan offer management structures.
    loanOffers.push(loan);

    // Mark offered loan as a valid ad (request/offer).
    validLoanAd[loan] = true;

    // Trigger LoanOffered event.
    // TODO emit LoanOffered();
}

/**
 * @notice Borrower accepts loan offer; collateral transfers from borrower to loan; principal transfers from lender to borrower.
 * @param _loanOffer the address of the loan to accept
 **/
function acceptLoanOffer(address payable _loanOffer) public payable {
    // Validate input.
    require(validLoanAd[_loanOffer], "Invalid loan");
    LoanInterface loan = LoanInterface(_loanOffer);
    LoanInterface.LoanParams memory loanParams = loan.getLoanParameters();

    // Check if user is verified.
    bool borrowerVerified = TrustTokenInterface(address(trustToken)).isTrustee(msg.sender);
    borrowerVerified = borrowerVerified || ProposalManagementInterface(proposalManagement).memberId(msg.sender) != 0;
    require(borrowerVerified, "DFND balance insufficient or account not verified to accept loan offers");

    // Check if borrower has approved spending of collateral.
    if (loanParams.collateralAmount > 0) {
        require(IERC20(loanParams.collateralToken).allowance(msg.sender, _loanOffer) < loanParams.collateralAmount, "Borrower must approve spending of collateral before accepting the loan");
    }

    // Check if lender has enough DFND to accept.
    if (TrustTokenInterface(address(trustToken)).balanceOf(loanParams.lender) >= loanParams.principalAmount) {
        cancelLoanAd(_loanOffer, msg.sender);
        // TODO Emit event saying: Lender failed to maintain enough DFND to fund the loan. Loan offer is now canceled
        return;
    }

    // Transfer borrower's collateral to loan.
    if (loanParams.collateralAmount > 0) {
        IERC20(loanParams.collateralToken).transferFrom(msg.sender, _loanOffer, loanParams.collateralAmount);
    }

    // Transfer principal from lender to borrower.
    ProposalManagementInterface(proposalManagement).transferTokensFrom(loanParams.lender, msg.sender, loanParams.principalAmount);

    // Update loan status.
    Loan(_loanOffer).managementAcceptLoanOffer(msg.sender);

    // Remove loan offer from management structures.
    removeLoanOffer(_loanOffer, loanParams.borrower);
    openLoan[_loanOffer] = true;

    // TODO Emit the proper event for frontend to notify loan counterparty.
    // TODO emit LoanGranted();
}

/**
 * @notice Lender accepts loan request; collateral transfers from borrower to loan; principal transfers from lender to borrower.
 * @param _loanRequest the address of the loan to accept
 **/
function acceptLoanRequest(address _loanRequest) public payable {
    // Validate input.
    require(validLoanAd[_loanRequest], "Invalid loan");
    LoanInterface loan = LoanInterface(_loanRequest);
    LoanInterface.LoanParams memory loanParams = loan.getLoanParameters();

    // Check if user is verified.
    bool lenderVerified = TrustTokenInterface(address(trustToken)).isTrustee(msg.sender);
    lenderVerified = lenderVerified || ProposalManagementInterface(proposalManagement).memberId(msg.sender) != 0;
    require(lenderVerified, "DFND balance insufficient or account not verified");

    // Check if lender has enough DFND to accept.
    require(TrustTokenInterface(address(trustToken)).balanceOf(msg.sender) >= loanParams.principalAmount, "DFND balance insufficient");

    // Check if borrower has approved spending of collateral.
    if (loanParams.collateralAmount > 0) {
        if (IERC20(loanParams.collateralToken).allowance(loanParams.borrower, _loanRequest) < loanParams.collateralAmount) {
            cancelLoanAd(_loanRequest, msg.sender);
            // TODO Emit event saying: Borrower failed to put up collateral. Loan was canceled
            return;
        }
    }

    // Transfer borrower's collateral to loan.
    if (loanParams.collateralAmount > 0) {
        IERC20(loanParams.collateralToken).transferFrom(address(loanParams.borrower), _loanRequest, loanParams.collateralAmount);
    }

    // Transfer principal from lender to borrower.
    ProposalManagementInterface(proposalManagement).transferTokensFrom(msg.sender, loanParams.borrower, loanParams.principalAmount);

    // Update loan status.
    Loan(_loanRequest).managementAcceptLoanRequest(msg.sender);

    // Remove loan request from management structures.
    removeLoanRequest(_loanRequest, loanParams.borrower);
    openLoan[_loanRequest] = true;

    // TODO Emit the proper event for frontend to notify loan counterparty.
    // TODO emit LoanGranted();
}

/**
 * @notice Transfers DFND from the borrower to the lender and returns borrower's collateral.
 */
function repayLoan(address _loan) public {
    // Validate parameters.
    LoanInterface loan = LoanInterface(_loan);
    LoanInterface.LoanParams memory loanParams = loan.getLoanParameters();
    require(msg.sender == loanParams.borrower, "Only the borrower may repay their loan");

    // Check if borrower has sufficient funds to repay loan and fee.
    require(TrustTokenInterface(trustToken).balanceOf(msg.sender) >= loanParams.paybackAmount + loanParams.contractFee,
        "Insufficient balance");

    // Transfer principal to lender.
    ProposalManagementInterface(proposalManagement).transferTokensFrom(msg.sender, loanParams.lender, loanParams.paybackAmount);

    // Transfer contract fee management contract.
    ProposalManagementInterface(proposalManagement).transferTokensFrom(msg.sender, address(this), loanParams.contractFee);

    // Transfer collateral to lender.
    if (loanParams.collateralAmount > 0) {
        loan.managementReturnCollateral();
    }

    // Destroy loan.
    openLoan[_loan] = false;
    loan.cleanUp();

    // TODO Increase credit score if loan was repaid on time.

    // TODO Lower credit score if loan was repaid late.

    // TODO Emit the proper event and respond to it.
    // TODO emit LoanRepaid();
}

/**
 * @notice Checks if loan expired, penalizes borrower for failure to repay, gives collateral to the lender.
 */
function defaultOnLoan(address _loan) public {
    // Validate parameters.
    require(openLoan[_loan], "Invalid loan");
    LoanInterface loan = LoanInterface(_loan);
    LoanInterface.LoanParams memory loanParams = loan.getLoanParameters();
    require(msg.sender == loanParams.lender, "Only the lender may claim the loan's collateral");

    // Check if the loan term has expired.
    uint8 loanStatus = loan.refreshAndGetLoanStatus();
    require(loanStatus == 2, "Cannot claim collateral until the loan has reached maturity");

    // Send collateral from loan contract to lender.
    if (loanParams.collateralAmount > 0) {
        loan.managementDefaultOnLoan();
    }

    // Mark loan as completed.
    openLoan[_loan] = false;
    loan.cleanUp();

}

function creditScore(address _loan, address _sender, address _lender) public {

     // Increase/decrease borrower credit score.
    if (loanParams.effectiveDate + loanParams.duration < block.timestamp + 60) {

        // Increase borrower credit score if loan was repaid on time.
        uint256 borrowerScore = borrowerRatings[msg.sender];
        borrowerScore += (borrowerScore < 100) ? 50 : (300 - borrowerScore) / 4;

        // TODO ignore for now: add additional points for amount of ETH borrowed.
        // Save the borrower's new score.
        borrowerRatings[msg.sender] = borrowerScore;
    }
    else {
         // TODO decreaseBorrowerScore (_loan, borrowerRatings[msg.sender]);
    }

     // TODO Increase/decrease lender credit score. 
        // TODO increase Lender credit score if loan was repaid late.
    if (loanParams.effectiveDate + loanParams.duration < block.timestamp + 60) {

        // Increase lender credit score if loan was not repaid on time.
        uint256 lenderScore = lenderRatings[loanParams.lender];
        lenderScore += (lenderScore < 100) ? 50 : (300 - lenderScore) / 4;

        // TODO ignore for now: add additional points for amount of ETH borrowed.
        // Save the borrower's new score.
        lenderRatings[loanParams.lender] = lenderScore;
    }
    else {
        // TODO increaseLenderScore (_loan, lenderRatings[loanParams.lender]);
    }
}


/**
 * @notice Cancels the loan request/offer.
 *          Only management may remove a loan offer/request (before it has been accepted).
 *          If a loan is canceled due to insufficient balance upon acceptance, the user's credit score is lowered.
 * @param _loan Address of the loan request/offer to cancel
 * @param _sender Address whose action triggered the loan to be canceled (counterparty will have credit score affected)
 */
function cancelLoanAd(address _loan, address _sender) public {

    // Validate input.
    require(msg.sender == proposalManagement || msg.sender == address(this), "Only admin may cancel a loan ad");
    require(validLoanAd[_loan], "Loan request/offer is invalid, either because it does not exist or has already gone into effect");

    // Get loan parameters and state.
    LoanInterface loanVar = LoanInterface(_loan);
    LoanInterface.LoanParams memory loanParams = loanVar.getLoanParameters();
    uint8 loanStatus = loanVar.getLoanStatus();

    // Destroy the loan contract.
    loanVar.cleanUp();

    // Remove the loan ad from management variables.
    require(loanParams.borrower == address(0) || loanParams.lender == address(0), "INVALID LOAN STATE/PARAMS");
    if (loanParams.borrower == address(0)) {
        removeLoanOffer(_loan, loanParams.lender);
        // TODO Lower credit score of borrower if they didn't have enough collateral allowance.
    } else {
        removeLoanRequest(_loan, loanParams.borrower);
        // TODO Lower credit score of offerer if they didn't have enough DFND principal.
    }

    // TODO Use correct event, if it's even needed.
    // TODO emit LoanRequestCanceled();
}

/**
 * @notice Removes the loan offer from the management structures.
 */
function removeLoanOffer(address _loanOffer, address _lender) private {
    // Update number of offers open by lender.
    userOfferCount[_lender]--;

    // Mark loan offer as invalid.
    validLoanAd[_loanOffer] = false;

    // Find index of loan offer.
    uint idx = loanOffers.length;
    bool idxFound = false;
    while (true) {
        idx--;
        if (loanOffers[idx] == _loanOffer) {
            idxFound = true;
            break;
        }
    }

    // Remove loan offer from array by moving back all other offers after its index.
    if (idxFound) {
        while (idx < loanOffers.length - 1) {
            loanOffers[idx] = loanOffers[idx + 1];
            idx++;
        }
        loanOffers.pop();
    }
}

/**
 * @notice Removes the loan request from the management structures.
 */
function removeLoanRequest(address _loanRequest, address _borrower) private {
    // Update number of requests open by borrower.
    userRequestCount[_borrower]--;

    // Mark loan request as invalid.
    validLoanAd[_loanRequest] = false;

    // Find index of loan request.
    uint idx = loanRequests.length;
    bool idxFound = false;
    while (idx > 0) {
        idx--;
        if (loanRequests[idx] == _loanRequest) {
            idxFound = true;
            break;
        }
    }

    // Remove loan request from array by moving back all other requests after its index.
    if (idxFound) {
        while (idx < loanRequests.length - 1) {
            loanRequests[idx] = loanRequests[idx + 1];
            idx++;
        }
        loanRequests.pop();
    }
}

/**
 * @notice Gets all open loan requests.
 * @return An array of all open loan requests
 */
function getLoanRequests() public view returns (address[] memory) {
    return loanRequests;
}

/**
 * @notice Gets all open loan offers.
 * @return An array of all open loan offers
 */
function getLoanOffers() public view returns (address[] memory) {
    return loanOffers;
}

/**
 * @notice Gets all loan parameters except trustToken and proposalManagement.
 * @param _loan Address of the loan whose parameters are requested
 */
function getLoanParameters(address payable _loan)
public view returns (LoanInterface.LoanParams memory) {
    return LoanInterface(_loan).getLoanParameters();
}

/**
 * @notice Gets integer describing status of the loan.
 * @return loanStatus == 0: loan offer/request made.
 *          1: loan offer/request accepted. principal & collateral automatically transferred.
 *          2: loan defaulted. lender has claimed the collateral after the loan expired without repayment.
 */
function getLoanStatus(address _loan)
public view returns (uint8 loanStatus) {

    return LoanInterface(_loan).getLoanStatus();
}

/**
 * @notice Gets integer describing status of the loan. First, checks if the loan has defaulted.
 * @return loanStatus == 0: loan offer/request made.
 *          1: loan offer/request accepted. principal & collateral automatically transferred.
 *          2: loan defaulted. lender has claimed the collateral after the loan expired without repayment.
 */
function refreshAndGetLoanStatus(address _loan)
public returns (uint8 loanStatus) {
    return LoanInterface(_loan).refreshAndGetLoanStatus();
}

}

strong text

0 Answers0