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
);
}
}