Skip to content

Instantly share code, notes, and snippets.

@asselstine
Created November 1, 2018 11:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save asselstine/a2ad657c3acafaa957687e50cb7e1b01 to your computer and use it in GitHub Desktop.
Save asselstine/a2ad657c3acafaa957687e50cb7e1b01 to your computer and use it in GitHub Desktop.
Work Contract supported deposits and withdrawals
pragma solidity ^0.4.24;
import 'openzeppelin-solidity/contracts/ownership/Ownable.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20.sol';
import './IndexedAddressArray.sol';
contract Work is Ownable {
using IndexedAddressArray for IndexedAddressArray.Data;
address public jobManager;
ERC20 public token;
IndexedAddressArray.Data stakers;
IndexedAddressArray.Data suspendedStakers;
uint256 public requiredStake;
uint256 public jobStake;
uint256 public stakeLimit;
mapping (address => uint256) public balances;
constructor (
ERC20 _token,
uint256 _requiredStake,
uint256 _jobStake,
uint256 _stakeLimit
) public {
require(_token != address(0), '_token is defined');
require(_requiredStake > 0, '_requiredStake is greater than zero');
require(_jobStake > 0, '_jobStake is greater than zero');
require(_stakeLimit > 0, '_stakeLimit is greater than zero');
token = _token;
requiredStake = _requiredStake;
jobStake = _jobStake;
stakeLimit = _stakeLimit;
}
function setStakeLimit(uint256 _stakeLimit) onlyOwner {
stakeLimit = _stakeLimit;
}
function setJobManager(address _jobManager) onlyOwner {
jobManager = _jobManager;
}
function depositStake() public {
require(token.allowance(msg.sender, address(this)) >= requiredStake, 'allowance is sufficient');
token.transferFrom(msg.sender, address(this), requiredStake);
deposit(msg.sender, requiredStake);
}
function withdrawStake() public hasStaked(msg.sender) {
if (stakers.hasAddress(msg.sender)) {
stakers.removeAddress(msg.sender);
}
if (suspendedStakers.hasAddress(msg.sender)) {
suspendedStakers.removeAddress(msg.sender);
}
uint256 amount = balances[msg.sender];
delete balances[msg.sender];
token.transfer(msg.sender, amount);
}
function withdrawJobStake(address _worker) external onlyJobManager returns (bool) {
require(balances[_worker] >= jobStake, 'worker has enough stake for job');
balances[_worker] -= jobStake;
token.transfer(msg.sender, jobStake);
/// If the new stake amount is below the job amount, suspend them
if (balances[_worker] < jobStake) {
stakers.removeAddress(_worker);
suspendedStakers.pushAddress(_worker);
}
return true;
}
function depositJobStake(address _worker) external onlyJobManager {
token.transferFrom(msg.sender, address(this), jobStake);
deposit(_worker, jobStake);
}
function selectWorker(uint256 randomValue) public view returns (address) {
uint256 index = randomValue % stakers.addresses.length;
return stakers.addresses[index];
}
function isStaker(address _staker) public view returns (bool) {
return stakers.hasAddress(_staker) || suspendedStakers.hasAddress(_staker);
}
function isSuspended(address _staker) public view returns (bool) {
return suspendedStakers.hasAddress(_staker);
}
function deposit(address _worker, uint256 _amount) internal {
uint256 newBalance = balances[_worker] + _amount;
require(newBalance <= stakeLimit, 'stake is below the limit');
balances[_worker] = newBalance;
if (balances[_worker] >= jobStake) {
if (suspendedStakers.hasAddress(_worker)) {
suspendedStakers.removeAddress(_worker);
}
if (!stakers.hasAddress(_worker)) {
stakers.pushAddress(_worker);
}
}
}
modifier hasNotStaked(address _staker) {
require(!isStaker(_staker), 'Staker has not staked');
_;
}
modifier hasStaked(address _staker) {
require(isStaker(_staker), 'Staker has staked');
_;
}
modifier onlyJobManager() {
require(msg.sender == jobManager, 'sender is job manager');
_;
}
}
library IndexedAddressArray {
struct Data {
address[] addresses;
mapping(address => uint256) indices;
}
function pushAddress(Data storage self, address _value) internal returns (uint256) {
uint256 index = self.addresses.push(_value) - 1;
self.indices[_value] = index;
return index;
}
function removeAddress(Data storage self, address _value) internal {
uint256 index = self.indices[_value];
delete self.indices[_value];
uint256 lastIndex = self.addresses.length - 1;
if (index != lastIndex) {
address lastValue = self.addresses[lastIndex];
self.addresses[index] = lastValue;
self.indices[lastValue] = index;
}
self.addresses.length--;
}
function hasAddress(Data storage self, address _value) internal returns (bool) {
return self.indices[_value] != 0 ||
(self.addresses.length > 0 && self.addresses[0] == _value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment