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
- Lease Agreement
- Landlord deploys contract with
rentAmount
,securityDeposit
, andleaseDuration
. - Tenant signs by paying 1st month + deposit (
signLease()
).
- Landlord deploys contract with
- Monthly Payments
- Tenant pays via ETH (
payRent()
) or USDT (payRentERC20()
). - Late payments incur penalties (off-chain tracking recommended).
- Tenant pays via ETH (
- Security Deposit
- Held in escrow until lease ends.
- Landlord can deduct for damages (
refundDeposit()
).
- 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
Relate to this article :