pragma solidity ^0.5.1;
// Interface for interacting with Compound
interface CompoundContract {
function supply (address asset, uint256 amount) external returns (uint256);
function withdraw (address asset, uint256 requestedAmount) external returns (uint256);
}
// Interface for interacting with tokens
interface token {
function transfer(address _to, uint256 _value) external returns (bool success) ;
function approve(address _spender, uint256 _value) external returns (bool);
function balanceOf(address owner) external returns (uint256);
}
// Basic ownership library
contract owned {
address public owner;
constructor() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) onlyOwner public {
owner = newOwner;
}
}
// Here we start
contract CompoundPayroll is owned {
// first, let's define the contracts we'll be interacting with
address compoundAddress = 0x3FDA67f7583380E67ef93072294a7fAc882FD7E7;
address daiAddress = 0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359;
CompoundContract compound = CompoundContract(compoundAddress);
token dai = token(daiAddress);
// Now let's create a payroll object
Salary[] public payroll;
mapping (address => uint) public salaryId;
uint public payrollLength;
struct Salary {
address recipient;
uint payRate;
uint lastPaid;
string name;
}
// An event for easier accounting
event MemberPaid(address recipient, uint amount, string justification);
// The constructor is called when you init the contract
constructor() public {
owner = msg.sender;
dai.approve(compoundAddress, 2 ** 128);
changePay(address(0), 0, now, '');
}
// Sends all the current balance to Compound
function putInSavings() public {
compound.supply(daiAddress, dai.balanceOf(address(this)));
}
// Allows owner to make specific payments
function cashOut (uint256 amount, address recipient, string memory justification) public onlyOwner {
compound.withdraw(daiAddress, amount);
dai.transfer(recipient, amount);
emit MemberPaid( recipient, amount, justification);
}
// Allows you to add or change a salary
function changePay(address recipient, uint yearlyPay, uint startingDate, string memory initials) onlyOwner public {
// check someone's salary ID
uint id = salaryId[recipient];
// if it returns 0 then create a new one
if (id == 0) {
salaryId[recipient] = payroll.length;
id = payroll.length++;
}
payroll[id] = Salary({
recipient: recipient,
payRate: yearlyPay / 365.25 days,
lastPaid: startingDate > 0 ? startingDate : now,
name: initials});
payrollLength = payroll.length;
}
// Removes a salary from the list
function removePay(address recipient) onlyOwner public {
require(salaryId[recipient] != 0);
for (uint i = salaryId[recipient]; i<payroll.length-1; i++){
payroll[i] = payroll[i+1];
salaryId[payroll[i].recipient] = i;
}
salaryId[recipient] = 0;
delete payroll[payroll.length-1];
payroll.length--;
payrollLength = payroll.length;
}
// How much are you owed right now?
function getAmountOwed(address recipient) view public returns (uint256) {
// get salary ID
uint id = salaryId[recipient];
if (id > 0) {
// If it exists, calculate how much you're owed right now
return (now - payroll[id].lastPaid) * payroll[id].payRate;
} else {
return 0;
}
}
//Make one salary payment
function paySalary(address recipient, string memory justification) public {
// How much are you owed right now?
uint amount = getAmountOwed(recipient);
if (amount == 0) return;
// Take it out from savings
compound.withdraw(daiAddress, amount);
// Pay it out
payroll[salaryId[recipient]].lastPaid = now;
dai.transfer(recipient, amount);
emit MemberPaid( recipient, amount, justification);
}
// Pay all salaries
function payEveryone() public {
for (uint i = 1; i<payroll.length; i++){
paySalary(payroll[i].recipient, 'payAll');
}
}
// pay everyone!
function payAll() public {
uint totalToPay = 0;
uint[] memory payments = new uint[](payrollLength);
uint amount;
for (uint i = 1; i<payrollLength; i++){
amount = (now - payroll[i].lastPaid) * payroll[i].payRate;
totalToPay += amount;
payments[i] = amount;
}
compound.withdraw(daiAddress, totalToPay);
for (uint i = 1; i<payrollLength; i++){
payroll[i].lastPaid = now;
dai.transfer(payroll[i].recipient, payments[i]);
emit MemberPaid(payroll[i].recipient, payments[i], 'payAll');
}
}
// If pinged, save and pay everyone
function () external payable {
putInSavings();
payAll();
msg.sender.transfer(msg.value);
}
}
{
"compilationTarget": {
"CompoundPayroll.sol": "CompoundPayroll"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"payroll","outputs":[{"name":"recipient","type":"address"},{"name":"payRate","type":"uint256"},{"name":"lastPaid","type":"uint256"},{"name":"name","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"recipient","type":"address"}],"name":"getAmountOwed","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"salaryId","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"payAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"recipient","type":"address"},{"name":"yearlyPay","type":"uint256"},{"name":"startingDate","type":"uint256"},{"name":"initials","type":"string"}],"name":"changePay","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"putInSavings","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"payrollLength","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"},{"name":"recipient","type":"address"},{"name":"justification","type":"string"}],"name":"cashOut","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"recipient","type":"address"}],"name":"removePay","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"recipient","type":"address"},{"name":"justification","type":"string"}],"name":"paySalary","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"payEveryone","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"justification","type":"string"}],"name":"MemberPaid","type":"event"}]