Skip to content

Instantly share code, notes, and snippets.

@joshterrill
Last active April 30, 2018 12:36
Show Gist options
  • Save joshterrill/ab953ebeb36a3dba06dc893d41101de6 to your computer and use it in GitHub Desktop.
Save joshterrill/ab953ebeb36a3dba06dc893d41101de6 to your computer and use it in GitHub Desktop.
Payroll solidity contract example
contract Payroll {
// define variables
address public CompanyOwner;
uint employeeId;
// create Employee object
struct Employee {
uint employeeId;
address employeeAddress;
uint wages;
uint balance;
}
// employees contain Employee
Employee[] public employees;
// run this on contract creation just once
function Payroll() {
// make CompanyOwner the person who deploys the contract
CompanyOwner = msg.sender;
employeeId = 0;
// add the company owner as an employee with a wage of 0
AddEmployee(msg.sender, 0);
}
// function for checking number of employees
function NumberOfEmployees() returns (uint _numEmployees) {
return employeeId;
}
// allows function initiator to withdraw funds if they're an employee equal to their balance
function WithdrawPayroll() returns (bool _success) {
var employeeId = GetCurrentEmployeeId();
if (employeeId != 999999) {
// they are an employee
if (employees[employeeId].balance > 0) {
// if they have a balance
if (this.balance >= employees[employeeId].balance) {
// if the balance of the contract is greater than or equal to employee balance
// then send them the money from the contract and set balance back to 0
msg.sender.send(employees[employeeId].balance);
employees[employeeId].balance = 0;
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
function GetCurrentEmployeeId() returns (uint _employeeId) {
// loop through employees
for (var i = 0; i < employees.length; i++) {
// if the initiator of the function's address exists in the employeeAddress area of an Employee, return ID
if (msg.sender == employees[i].employeeAddress) {
return employees[i].employeeId;
}
}
return 999999;
}
// function for getting an ID by address if needed
function GetEmployeeIdByAddress(address _employee) returns (uint _employeeId) {
for (var i = 0; i < employees.length; i++) {
if (_employee == employees[i].employeeAddress) {
return employees[i].employeeId;
}
}
return 999999;
}
/* OWNER ONLY FUNCTIONS */
// add an employee given an address and wages
function AddEmployee(address _employee, uint _wages) returns (bool _success) {
if (msg.sender != CompanyOwner) {
return false;
} else {
employees.push(Employee(employeeId, _employee, _wages, 0));
employeeId++;
return true;
}
}
// pay the employee their wages given an employee ID
function PayEmployee(uint _employeeId) returns (bool _success) {
if (msg.sender != CompanyOwner) {
return false;
} else {
// TODO: need to figure out how to check to make sure employees[_employeeId] exists
employees[_employeeId].balance += employees[_employeeId].wages;
return true;
}
}
// modify the employee wages given an employeeId and a new wage
function ModifyEmployeeWages(uint _employeeId, uint _newWage) returns (bool _success) {
if (msg.sender != CompanyOwner) {
return false;
} else {
// TODO: need to figure out how to check to make sure employees[_employeeId] exists
// change employee wages to new wage
employees[_employeeId].wages = _newWage;
return true;
}
}
// check how much money is in the contract
function CheckContractBalance() returns (uint _balance) {
return this.balance;
}
}
@joeb000
Copy link

joeb000 commented Aug 23, 2016

Solid first contract! I ran it on my testnet and it worked as described :)

Just a couple of things I'd look into changing:

  1. Use modifiers for the admin function - function modifiers are really useful for slimming down solidity code, they are perfect for checking if the caller is admin/owner.
  2. See the solidity style guide for best practices on naming conventions...I know you don't have any events in your contract yet, but once you do its going to get confusing if you also capitalize your function names. http://solidity.readthedocs.io/en/latest/style-guide.html?highlight=modifiers#naming-conventions
  3. Your GetCurrentEmployeeId() method is very inefficient and gets more expensive (gas cost) each time you add a new employee. A better way to find a callers own employee ID would be to save the employee ID in a mapping with a key of their address: mapping (address => uint) public employeeIDs this way you can just find the callers ID by doing: employeeIDs[msg.sender] - no need for the expensive for loop! This will help in a lot of your other methods too.

@joeb000
Copy link

joeb000 commented Aug 24, 2016

I stripped it down, took out the unneccessary methods (use public variable instead of coded accessors), and added a removeEmployee() method.

contract Payroll {

    address public owner;
    uint public nextEmpolyeeID;
    struct Employee {
        uint employeeId;
        uint wages;
        uint balance;
    }

    mapping (address => Employee) employees;

    modifier onlyOwner {
        if (msg.sender==owner)
        _
    }

    function Payroll() {
        owner = msg.sender;
        addEmployee(msg.sender, 0);
    }

    // allows function initiator to withdraw funds if they're an employee equal to their balance
    function withdrawPayroll() returns (bool _success) {
        uint empBalance = employees[msg.sender].balance;
        if (empBalance > 0 && this.balance >= empBalance) {
                if (msg.sender.send(empBalance)){
                    employees[msg.sender].balance = 0;
                    return true;
            }
        }
        return false;
    }

    // function for getting an ID by address if needed
    function getEmployeeIdByAddress(address _employee) returns (uint _employeeId) {
        return employees[_employee].employeeId;
    }

    /* OWNER ONLY FUNCTIONS */
    // add an employee given an address and wages
    function addEmployee(address _employee, uint _wages) onlyOwner returns (bool _success) {
        nextEmpolyeeID++;
        employees[_employee].employeeId=nextEmpolyeeID;
        employees[_employee].wages=_wages;
        //balance is 0 by default
        return true;
    }

    function removeEmployee(address _employee) onlyOwner returns (bool _success) {
        employees[_employee].wages=0;
        employees[_employee].employeeId=0;
        return true;
    }

    // pay the employee their wages given an employee ID
    function payEmployee(address _employeeId) onlyOwner returns (bool _success) {
            employees[_employeeId].balance += employees[_employeeId].wages;
            //keep their balance so they can escape with their dignity
            return true;
    }

    // modify the employee wages given an employeeId and a new wage
    function modifyEmployeeWages(address _employeeId, uint _newWage) onlyOwner returns (bool _success) {
        employees[_employeeId].wages = _newWage;
        return true;
    }
}

@joeb000
Copy link

joeb000 commented Aug 24, 2016

and actually, you don't even need to have such thing as an employeeId in your struct...each address is unique so its safe to use this as a unique identifier for employees...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment