5 minute read

// 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 mapping for 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 wei per 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 totalPenalties and 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