Income-Based Loan Repayment Contract

What it does:
A smart contract that allows borrowers to repay loans dynamically based on their income, rather than fixed monthly payments, making debt more flexible and humane.

Why it matters:
Traditional loans ignore real-life income fluctuations. This contract adjusts repayments automatically, reducing default risk while protecting borrowers during low-income periods and ensuring lenders are repaid fairly over time.

How it works:

  • A lender funds a loan vault for a borrower.

  • The borrower periodically reports income (via oracle or trusted verifier).

  • Repayment amount is calculated as a percentage of reported income.

  • Payments are enforced on-chain and sent directly to the lender.

  • Once the full loan amount is repaid, the loan is closed automatically.

      // SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title IncomeBasedLoan
 * @author Nam
 * @notice Loan repayments are calculated as a percentage of borrower income
 */
contract IncomeBasedLoan {

    address public lender;
    address public borrower;

    uint256 public loanAmount;
    uint256 public totalRepaid;
    uint256 public incomeShare; // percentage (e.g. 10 = 10%)
    uint256 public loanStart;
    uint256 public loanDuration;

    bool public loanActive;

    event LoanFunded(address indexed lender, uint256 amount);
    event IncomeReported(address indexed borrower, uint256 income);
    event RepaymentMade(address indexed borrower, uint256 amount);
    event LoanCompleted(address indexed borrower);

    modifier onlyBorrower() {
        require(msg.sender == borrower, "Not borrower");
        _;
    }

    modifier onlyLender() {
        require(msg.sender == lender, "Not lender");
        _;
    }

    constructor(
        address _borrower,
        uint256 _incomeShare,
        uint256 _loanDuration
    ) {
        require(_borrower != address(0), "Invalid borrower");
        require(_incomeShare > 0 && _incomeShare  0, "Loan amount required");

        loanAmount = msg.value;
        loanStart = block.timestamp;
        loanActive = true;

        payable(borrower).transfer(msg.value);

        emit LoanFunded(msg.sender, msg.value);
    }

    /**
     * @notice Borrower reports income and triggers repayment
     */
    function reportIncome(uint256 _income) external payable onlyBorrower {
        require(loanActive, "Loan inactive");
        require(_income > 0, "Income must be positive");

        uint256 repayment = (_income * incomeShare) / 100;
        require(msg.value == repayment, "Incorrect repayment amount");

        totalRepaid += repayment;
        payable(lender).transfer(repayment);

        emit IncomeReported(msg.sender, _income);
        emit RepaymentMade(msg.sender, repayment);

        if (totalRepaid >= loanAmount) {
            loanActive = false;
            emit LoanCompleted(msg.sender);
        }
    }

    /**
     * @notice Emergency close if loan duration expires
     */
    function forceClose() external onlyLender {
        require(loanActive, "Loan inactive");
        require(block.timestamp >= loanStart + loanDuration, "Loan still active");

        loanActive = false;
        emit LoanCompleted(borrower);
    }

    /**
     * @notice View loan status
     */
    function getLoanStatus()
        external
        view
        returns (
            uint256 _loanAmount,
            uint256 _totalRepaid,
            uint256 _incomeShare,
            bool _active
        )
    {
        return (
            loanAmount,
            totalRepaid,
            incomeShare,
            loanActive
        );
    }
}