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