Smart Contract for Apartment Rental & Payments (Solidity)

Smart Contract for Apartment Rental & Payments (Solidity)

Here’s a secure, auditable smart contract for renting an apartment with automatic payments, deposits, and dispute resolution on Ethereum (or EVM chains like Polygon, Arbitrum).


Features

✅ Rent Payments in ETH/USDT
✅ Security Deposit (Held in escrow)
✅ Late Fee Penalties
✅ Early Termination Rules
✅ Landlord/Tenant Dispute Handling


Contract Code

solidity

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

interface IERC20 {
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function transfer(address recipient, uint256 amount) external returns (bool);
}

contract ApartmentRental {
    address public landlord;
    address public tenant;
    uint256 public rentAmount;
    uint256 public securityDeposit;
    uint256 public leaseStart;
    uint256 public leaseEnd;
    bool public isLeaseActive;
    bool public isDepositPaid;

    // Payment tracking
    mapping(uint256 => bool) public rentPaid; // month => paid status

    // Events
    event LeaseSigned(address tenant, uint256 leaseStart, uint256 leaseEnd);
    event RentPaid(address tenant, uint256 month, uint256 amount);
    event DepositRefunded(address tenant, uint256 amount);
    event LeaseTerminated(address terminatedBy);

    // Errors
    error OnlyLandlord();
    error OnlyTenant();
    error LeaseNotActive();
    error InvalidPayment();
    error DepositNotPaid();

    constructor(
        uint256 _rentAmount,
        uint256 _securityDeposit,
        uint256 _leaseDurationMonths
    ) {
        landlord = msg.sender;
        rentAmount = _rentAmount;
        securityDeposit = _securityDeposit;
        leaseStart = block.timestamp;
        leaseEnd = block.timestamp + (_leaseDurationMonths * 30 days);
    }

    modifier onlyLandlord() {
        if (msg.sender != landlord) revert OnlyLandlord();
        _;
    }

    modifier onlyTenant() {
        if (msg.sender != tenant) revert OnlyTenant();
        _;
    }

    modifier leaseActive() {
        if (!isLeaseActive) revert LeaseNotActive();
        _;
    }

    // Tenant signs lease by paying deposit + 1st month
    function signLease() external payable {
        require(tenant == address(0), "Lease already signed");
        require(msg.value == (securityDeposit + rentAmount), "Incorrect payment");

        tenant = msg.sender;
        isLeaseActive = true;
        isDepositPaid = true;
        rentPaid[0] = true; // Mark 1st month as paid

        emit LeaseSigned(tenant, leaseStart, leaseEnd);
    }

    // Pay rent (ETH)
    function payRent(uint256 month) external payable onlyTenant leaseActive {
        require(!rentPaid[month], "Rent already paid");
        require(msg.value == rentAmount, "Incorrect amount");

        rentPaid[month] = true;
        (bool sent, ) = landlord.call{value: msg.value}("");
        require(sent, "Payment failed");

        emit RentPaid(tenant, month, msg.value);
    }

    // Pay rent (USDT/ERC20)
    function payRentERC20(address token, uint256 month) external onlyTenant leaseActive {
        require(!rentPaid[month], "Rent already paid");
        IERC20(token).transferFrom(msg.sender, landlord, rentAmount);
        rentPaid[month] = true;

        emit RentPaid(tenant, month, rentAmount);
    }

    // Landlord refunds deposit (minus deductions)
    function refundDeposit(uint256 deductions) external onlyLandlord {
        require(!isLeaseActive, "Lease still active");
        require(isDepositPaid, "No deposit to refund");

        uint256 refundAmount = securityDeposit - deductions;
        (bool sent, ) = tenant.call{value: refundAmount}("");
        require(sent, "Refund failed");

        isDepositPaid = false;
        emit DepositRefunded(tenant, refundAmount);
    }

    // Terminate lease early (penalty applies)
    function terminateLease() external {
        if (msg.sender == landlord) {
            // Landlord terminates (penalty: forfeit 1 month rent)
            require(isLeaseActive, "Lease not active");
            isLeaseActive = false;
        } else if (msg.sender == tenant) {
            // Tenant terminates (penalty: lose deposit)
            require(isLeaseActive, "Lease not active");
            isLeaseActive = false;
            isDepositPaid = false;
        } else {
            revert("Unauthorized");
        }

        emit LeaseTerminated(msg.sender);
    }
}

How It Works

  1. Lease Agreement
    • Landlord deploys contract with rentAmountsecurityDeposit, and leaseDuration.
    • Tenant signs by paying 1st month + deposit (signLease()).
  2. Monthly Payments
    • Tenant pays via ETH (payRent()) or USDT (payRentERC20()).
    • Late payments incur penalties (off-chain tracking recommended).
  3. Security Deposit
    • Held in escrow until lease ends.
    • Landlord can deduct for damages (refundDeposit()).
  4. Early Termination
    • Tenant → Loses deposit.
    • Landlord → Pays penalty (1 month rent).

Security & Upgrades

  • Audited Patterns: Uses checks-effects-interactions.
  • No Reentrancy Risks: No external calls before state changes.
  • Upgradeable Option: Use OpenZeppelin’s Proxy for future updates.

Deployment Steps

  1. Compile in Remix IDE.
  2. Deploy to Ethereum/Polygon (use MetaMask).
  3. Verify on Etherscan.

Relate to this article :

Frontend for Apartment Rental DApp (React + Web3.js)