Employee Management System (solidity)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract EmployeeManagement {
// Contract owner
address public owner;
// Employee structure
struct Employee {
address walletAddress;
uint256 baseSalary;
uint256 totalIncentives;
uint256 totalPenalties;
bool isActive;
mapping(uint256 => DailyRecord) dailyRecords;
uint256 totalDailyRecords;
}
// Daily attendance record
struct DailyRecord {
uint256 arrivalTime;
uint256 departureTime;
uint256 dailyPenalty;
bool isLate;
bool leftEarly;
}
// Mappings
mapping(address => Employee) public employees;
address[] public employeeAddresses;
// Penalty and time constants
uint256 public constant LATE_THRESHOLD = 5 minutes;
uint256 public constant DEPARTURE_THRESHOLD = 5 minutes;
uint256 public PENALTY_RATE_PER_MINUTE = 1000 wei;
// Expected work timings
uint256 public expectedArrivalTime;
uint256 public expectedDepartureTime;
// Events
event EmployeeAdded(address indexed employeeAddress);
event EmployeeRemoved(address indexed employeeAddress);
event SalaryUpdated(address indexed employeeAddress, uint256 newSalary);
event IncentiveAdded(address indexed employeeAddress, uint256 amount);
event AttendanceRecorded(address indexed employeeAddress, uint256 arrivalTime, uint256 departureTime);
event SalaryPaid(address indexed employeeAddress, uint256 amount);
// Modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
modifier onlyActiveEmployee() {
require(employees[msg.sender].isActive, "Employee is not active");
_;
}
// Constructor
constructor(uint256 _expectedArrivalTime, uint256 _expectedDepartureTime) {
owner = msg.sender;
expectedArrivalTime = _expectedArrivalTime;
expectedDepartureTime = _expectedDepartureTime;
}
// Owner functions
function addEmployee(address _employeeAddress, uint256 _baseSalary) public onlyOwner {
require(!employees[_employeeAddress].isActive, "Employee already exists");
Employee storage newEmployee = employees[_employeeAddress];
newEmployee.walletAddress = _employeeAddress;
newEmployee.baseSalary = _baseSalary;
newEmployee.isActive = true;
newEmployee.totalIncentives = 0; // Explicitly set to 0
employeeAddresses.push(_employeeAddress);
emit EmployeeAdded(_employeeAddress);
}
function removeEmployee(address _employeeAddress) public onlyOwner {
require(employees[_employeeAddress].isActive, "Employee not found");
employees[_employeeAddress].isActive = false;
emit EmployeeRemoved(_employeeAddress);
}
function updateEmployeeSalary(address _employeeAddress, uint256 _newSalary) public onlyOwner {
require(employees[_employeeAddress].isActive, "Employee not found");
employees[_employeeAddress].baseSalary = _newSalary;
emit SalaryUpdated(_employeeAddress, _newSalary);
}
function addIncentive(address _employeeAddress, uint256 _amount) public onlyOwner {
require(employees[_employeeAddress].isActive, "Employee not found");
// Explicitly add incentive, or keep it at 0 if no incentive is added
if (_amount > 0) {
employees[_employeeAddress].totalIncentives = _amount;
emit IncentiveAdded(_employeeAddress, _amount);
} else {
// Ensure incentives remain 0 if no amount is specified
employees[_employeeAddress].totalIncentives = 0;
}
}
// Employee functions
function recordArrival() public onlyActiveEmployee {
Employee storage employee = employees[msg.sender];
uint256 currentTime = block.timestamp;
// Check if employee is late
bool isLate = currentTime > expectedArrivalTime;
uint256 penalty = 0;
if (isLate) {
uint256 lateDuration = currentTime - expectedArrivalTime;
if (lateDuration > LATE_THRESHOLD) {
penalty = (lateDuration - LATE_THRESHOLD) * PENALTY_RATE_PER_MINUTE;
employee.totalPenalties += penalty;
}
}
// Record daily record
DailyRecord storage dailyRecord = employee.dailyRecords[employee.totalDailyRecords];
dailyRecord.arrivalTime = currentTime;
dailyRecord.isLate = isLate;
dailyRecord.dailyPenalty += penalty;
emit AttendanceRecorded(msg.sender, currentTime, 0);
}
function recordDeparture() public onlyActiveEmployee {
Employee storage employee = employees[msg.sender];
uint256 currentTime = block.timestamp;
// Ensure arrival is recorded
require(employee.dailyRecords[employee.totalDailyRecords].arrivalTime > 0, "Arrival not recorded");
// Check if employee left early
bool leftEarly = currentTime < expectedDepartureTime;
uint256 penalty = 0;
if (leftEarly) {
uint256 earlyDuration = expectedDepartureTime - currentTime;
if (earlyDuration > DEPARTURE_THRESHOLD) {
penalty = (earlyDuration - DEPARTURE_THRESHOLD) * PENALTY_RATE_PER_MINUTE;
employee.totalPenalties += penalty;
}
}
// Update daily record
DailyRecord storage dailyRecord = employee.dailyRecords[employee.totalDailyRecords];
dailyRecord.departureTime = currentTime;
dailyRecord.leftEarly = leftEarly;
dailyRecord.dailyPenalty += penalty;
// Increment daily records
employee.totalDailyRecords++;
emit AttendanceRecorded(msg.sender, dailyRecord.arrivalTime, currentTime);
}
// Salary payment function
function paySalary() public onlyActiveEmployee {
Employee storage employee = employees[msg.sender];
// Calculate final salary
uint256 finalSalary = employee.baseSalary + employee.totalIncentives - employee.totalPenalties;
// Transfer salary
payable(msg.sender).transfer(finalSalary);
// Reset incentives and penalties
employee.totalIncentives = 0;
employee.totalPenalties = 0;
emit SalaryPaid(msg.sender, finalSalary);
}
// View functions
function getEmployeeDetails(address _employeeAddress) public view returns (
bool isActive,
uint256 baseSalary,
uint256 totalIncentives,
uint256 totalPenalties
) {
Employee storage employee = employees[_employeeAddress];
return (
employee.isActive,
employee.baseSalary,
employee.totalIncentives,
employee.totalPenalties
);
}
function getAllEmployees() public view returns (address[] memory) {
return employeeAddresses;
}
// Fallback function
receive() external payable {}
}
🔐 Contract Overview
🔧 Purpose
This contract enables:
- Adding/removing employees.
- Recording arrival and departure times.
- Applying penalties for lateness or early departure.
- Paying salaries based on base salary, incentives, and penalties.
🧱 Struct Definitions
Employee
struct Employee {
address walletAddress;
uint256 baseSalary;
uint256 totalIncentives;
uint256 totalPenalties;
bool isActive;
mapping(uint256 => DailyRecord) dailyRecords;
uint256 totalDailyRecords;
}
- Stores personal salary-related and attendance data.
- Uses a
mappingfor each employee’s attendance (dailyRecords), indexed by a record count.
DailyRecord
struct DailyRecord {
uint256 arrivalTime;
uint256 departureTime;
uint256 dailyPenalty;
bool isLate;
bool leftEarly;
}
- Logs daily attendance info: when the employee arrived/left and if penalties were applied.
🌐 Global State Variables
address public owner;
mapping(address => Employee) public employees;
address[] public employeeAddresses;
owner: Admin who manages employees and system settings.employees: Maps addresses to employee records.employeeAddresses: List of all employee addresses for iteration.
uint256 public constant LATE_THRESHOLD = 5 minutes;
uint256 public constant DEPARTURE_THRESHOLD = 5 minutes;
uint256 public PENALTY_RATE_PER_MINUTE = 1000 wei;
- Penalty starts only after 5 minutes delay.
- Penalty rate is
1000 weiper extra minute (configurable).
uint256 public expectedArrivalTime;
uint256 public expectedDepartureTime;
- Target clock-in and clock-out times, set during deployment.
🔐 Access Control
modifier onlyOwner()
Restricts access to contract owner functions (e.g., addEmployee).
modifier onlyActiveEmployee()
Allows only registered and active employees to call functions like recordArrival().
🏁 Constructor
constructor(uint256 _expectedArrivalTime, uint256 _expectedDepartureTime)
- Initializes the contract and sets expected work times.
👷 Employee Management
addEmployee(...)
Registers a new employee:
- Requires address is not already active.
- Initializes salary, sets employee as active.
removeEmployee(...)
Marks employee as inactive (soft delete).
updateEmployeeSalary(...)
Updates base salary for an employee.
addIncentive(...)
Sets (or resets) incentive amount. Overwrites previous value, which could be a limitation.
⏱️ Attendance Tracking
recordArrival()
Logs arrival time and calculates lateness penalty:
- If arrival is later than expected, subtracts 5-minute grace.
- Applies
PENALTY_RATE_PER_MINUTE. - Updates employee’s
totalPenaltiesand attendance record.
recordDeparture()
Logs departure time and calculates early leave penalty:
- Must be called after arrival is recorded.
- If leaving earlier than expected, subtracts 5-minute grace.
- Updates penalties and moves to the next daily record.
💵 Salary Calculation & Payment
paySalary()
Transfers salary to employee:
finalSalary = baseSalary + incentives - penalties.- Uses
transfer()(safe for fixed gas limits). - Resets incentive and penalty amounts after payment.
📤 View Functions
getEmployeeDetails(address)
Returns employee’s status, salary, incentive, and penalty data.
getAllEmployees()
Returns an array of all employee addresses.
🔂 Fallback Function
receive() external payable {}
- Allows the contract to receive Ether, needed for salary payments.
📌 Key Design Observations
✅ Pros:
- Tracks attendance and salary on-chain.
- Penalizes tardiness or early departure with real Ether deductions.
- Incentives configurable by employer.
- Full audit trail via
DailyRecord.
📚 Summary Table
| Function | Who Can Call | Purpose |
|---|---|---|
addEmployee() |
Only Owner | Add a new employee |
removeEmployee() |
Only Owner | Deactivate employee |
updateEmployeeSalary() |
Only Owner | Change base salary |
addIncentive() |
Only Owner | Add bonuses |
recordArrival() |
Employee | Clock-in with lateness penalty |
recordDeparture() |
Employee | Clock-out with early-leave penalty |
paySalary() |
Employee | Receive salary payout |
getEmployeeDetails() |
Anyone | View employee summary |