Automated Rent Payment & Penalty
What it does:
A smart contract that automatically pulls rent payments on schedule and enforces penalties when payments are late, removing friction and disputes between landlord and tenant.
Why it matters:
Missed rent and manual reminders create stress and conflict. This contract enforces rent discipline programmatically, ensuring fairness and predictability for both sides.
How it works:
-
Tenant pre-funds the contract or approves spending.
-
Rent is automatically collected each rental period.
-
Late or insufficient balances trigger penalties.
-
Landlord can withdraw collected rent.
-
All payment activity is fully transparent on-chain.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title AutomatedRentPaymentAndPenalty
* @author Nam
* @notice Automated rent collection with penalties for late payments
*/
contract AutomatedRentPaymentAndPenalty {
// -------------------- STATE VARIABLES --------------------
address public landlord;
address public tenant;
uint256 public rentAmount; // Rent per period
uint256 public rentInterval; // Interval (e.g. 30 days)
uint256 public latePenalty; // Penalty per missed period
uint256 public lastPayment;
uint256 public escrowBalance;
bool public active;
// -------------------- EVENTS --------------------
event Funded(address indexed tenant, uint256 amount);
event RentCollected(uint256 amount);
event PenaltyApplied(uint256 penalty);
event Withdrawn(address indexed landlord, uint256 amount);
event AgreementTerminated();
// -------------------- MODIFIERS --------------------
modifier onlyTenant() {
require(msg.sender == tenant, "Not tenant");
_;
}
modifier onlyLandlord() {
require(msg.sender == landlord, "Not landlord");
_;
}
modifier agreementActive() {
require(active, "Agreement inactive");
_;
}
// -------------------- CONSTRUCTOR --------------------
constructor(
address _tenant,
uint256 _rentAmount,
uint256 _rentInterval,
uint256 _latePenalty
) {
require(_tenant != address(0), "Invalid tenant");
require(_rentAmount > 0, "Invalid rent");
require(_rentInterval > 0, "Invalid interval");
landlord = msg.sender;
tenant = _tenant;
rentAmount = _rentAmount;
rentInterval = _rentInterval;
latePenalty = _latePenalty;
}
// -------------------- START & FUND --------------------
/**
* @notice Tenant funds escrow and activates agreement
*/
function start() external payable onlyTenant {
require(!active, "Already active");
require(msg.value >= rentAmount, "Insufficient funding");
escrowBalance += msg.value;
lastPayment = block.timestamp;
active = true;
emit Funded(msg.sender, msg.value);
}
/**
* @notice Add more funds to escrow
*/
function fund() external payable onlyTenant agreementActive {
require(msg.value > 0, "Zero funding");
escrowBalance += msg.value;
emit Funded(msg.sender, msg.value);
}
// -------------------- AUTOMATED COLLECTION --------------------
/**
* @notice Collect rent if interval passed
*/
function collectRent() external agreementActive {
require(
block.timestamp >= lastPayment + rentInterval,
"Not due yet"
);
uint256 periodsMissed =
(block.timestamp - lastPayment) / rentInterval;
uint256 totalDue =
rentAmount * periodsMissed;
uint256 totalPenalty =
latePenalty * (periodsMissed - 1);
uint256 payableAmount =
totalDue + totalPenalty;
require(
escrowBalance >= payableAmount,
"Insufficient escrow"
);
escrowBalance -= payableAmount;
lastPayment = block.timestamp;
payable(landlord).transfer(payableAmount);
emit RentCollected(totalDue);
if (totalPenalty > 0) {
emit PenaltyApplied(totalPenalty);
}
}
// -------------------- LANDLORD WITHDRAW --------------------
/**
* @notice Withdraw remaining funds (lease end)
*/
function withdraw(uint256 _amount)
external
onlyLandlord
{
require(_amount <= address(this).balance, "Too much");
payable(landlord).transfer(_amount);
emit Withdrawn(msg.sender, _amount);
}
// -------------------- TERMINATION --------------------
/**
* @notice Terminate agreement
*/
function terminate()
external
onlyLandlord
agreementActive
{
active = false;
emit AgreementTerminated();
}
// -------------------- VIEW FUNCTIONS --------------------
function nextPaymentDue() external view returns (uint256) {
return lastPayment + rentInterval;
}
}