Utility Bill Sharing Contract
What it does:
Automatically splits and tracks shared utility bills (electricity, water, internet, gas) among multiple tenants and enforces fair, transparent payments.
Why it matters:
Eliminates disputes, late payments, and manual calculations by enforcing clear cost-sharing rules that everyone can verify on-chain.
How it works:
-
A utility bill group is created for a property or household
-
Participants and their payment shares are defined upfront
-
Monthly utility costs are submitted by the landlord or admin
-
Each participant’s payable amount is calculated automatically
-
Tenants pay their portion directly to the contract
-
Unpaid balances are transparently visible to all members
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title UtilityBillSharing
* @author Nam
* @notice Splits and manages shared utility bill payments on-chain
*/
contract UtilityBillSharing {
// -------------------- ROLES --------------------
address public admin;
// -------------------- PARTICIPANTS --------------------
address[] public participants;
mapping(address => uint256) public shares; // percentage-based (sum = 100)
// -------------------- BILL DATA --------------------
uint256 public currentBillAmount;
mapping(address => bool) public hasPaid;
bool public billActive;
// -------------------- EVENTS --------------------
event ParticipantAdded(address indexed participant, uint256 share);
event BillIssued(uint256 amount);
event PaymentReceived(address indexed payer, uint256 amount);
event BillSettled();
// -------------------- MODIFIERS --------------------
modifier onlyAdmin() {
require(msg.sender == admin, "Not admin");
_;
}
modifier onlyParticipant() {
require(shares[msg.sender] > 0, "Not participant");
_;
}
modifier billOpen() {
require(billActive, "No active bill");
_;
}
// -------------------- CONSTRUCTOR --------------------
constructor(address[] memory _participants, uint256[] memory _shares) {
require(_participants.length == _shares.length, "Length mismatch");
require(_participants.length > 0, "No participants");
admin = msg.sender;
uint256 totalShare;
for (uint256 i = 0; i 0, "Invalid share");
participants.push(_participants[i]);
shares[_participants[i]] = _shares[i];
totalShare += _shares[i];
emit ParticipantAdded(_participants[i], _shares[i]);
}
require(totalShare == 100, "Total share must be 100");
}
// -------------------- BILL LOGIC --------------------
/**
* @notice Issue a new utility bill
*/
function issueBill(uint256 _amount) external onlyAdmin {
require(!billActive, "Previous bill active");
require(_amount > 0, "Invalid bill amount");
currentBillAmount = _amount;
billActive = true;
for (uint256 i = 0; i < participants.length; i++) {
hasPaid[participants[i]] = false;
}
emit BillIssued(_amount);
}
/**
* @notice Pay individual share of the utility bill
*/
function payBill() external payable onlyParticipant billOpen {
require(!hasPaid[msg.sender], "Already paid");
uint256 requiredPayment =
(currentBillAmount * shares[msg.sender]) / 100;
require(msg.value == requiredPayment, "Incorrect payment");
hasPaid[msg.sender] = true;
emit PaymentReceived(msg.sender, msg.value);
if (address(this).balance == currentBillAmount) {
billActive = false;
payable(admin).transfer(currentBillAmount);
emit BillSettled();
}
}
// -------------------- VIEW FUNCTIONS --------------------
/**
* @notice Check how much a participant owes
*/
function amountDue(address _participant)
external
view
returns (uint256)
{
if (!billActive || hasPaid[_participant]) {
return 0;
}
return (currentBillAmount * shares[_participant]) / 100;
}
}