合约地址:https://etherscan.io/address/0x1cf33ead073351b3fc63c1f4f35dc10791fe6c7c#code
pragma solidity 0.4.24;
contract Ownable {
address public owner;
event OwnershipRenounced(address indexed previousOwner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipRenounced(owner);
owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function _transferOwnership(address _newOwner) internal {
require(_newOwner != address(0));
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
// Gas optimization: this is cheaper than asserting 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
// uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return a / b;
}
/**
* @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
assert(c >= a);
return c;
}
}
contract BGAudit is Ownable {
using SafeMath for uint;
event AddedAuditor(address indexed auditor);
event BannedAuditor(address indexed auditor);
event AllowedAuditor(address indexed auditor);
event CreatedAudit(uint indexed id);
event ReviewingAudit(uint indexed id);
event AuditorRewarded(uint indexed id, address indexed auditor, uint indexed reward);
event AuditorStaked(uint indexed id, address indexed auditor, uint indexed amount);
event WithdrawedStake(uint indexed id, address indexed auditor, uint indexed amount);
event SlashedStake(uint indexed id, address indexed auditor);
enum AuditStatus { New, InProgress, InReview, Completed }
struct Auditor {
bool banned;
address addr;
uint totalEarned;
uint completedAudits;
uint[] stakedAudits; // array of audit IDs they've staked
mapping(uint => bool) stakedInAudit; // key is AuditID; useful so we don't need to loop through the audits array above
mapping(uint => bool) canWithdrawStake; // Audit ID => can withdraw stake or not
}
struct Audit {
AuditStatus status;
address owner;
uint id;
uint totalReward; // total reward shared b/w all auditors
uint remainingReward; // keep track of how much reward is left
uint stake; // required stake for each auditor in wei
uint endTime; // scheduled end time for the audit
uint maxAuditors; // max auditors allowed for this Audit
address[] participants; // array of auditor that have staked
}
//=== Storage
uint public stakePeriod = 90 days; // number of days to wait before stake can be withdrawn
uint public maxAuditDuration = 365 days; // max amount of time for a security audit
Audit[] public audits;
mapping(address => Auditor) public auditors;
//=== Owner related
function transfer(address _to, uint _amountInWei) external onlyOwner {
require(address(this).balance > _amountInWei);
_to.transfer(_amountInWei);
}
function setStakePeriod(uint _days) external onlyOwner {
stakePeriod = _days * 1 days;
}
function setMaxAuditDuration(uint _days) external onlyOwner {
maxAuditDuration = _days * 1 days;
}
//=== Auditors
function addAuditor(address _auditor) external onlyOwner {
require(auditors[_auditor].addr == address(0)); // Only add if they're not already added
auditors[_auditor].banned = false;
auditors[_auditor].addr = _auditor;
auditors[_auditor].completedAudits = 0;
auditors[_auditor].totalEarned = 0;
emit AddedAuditor(_auditor);
}
function banAuditor(address _auditor) external onlyOwner {
require(auditors[_auditor].addr != address(0));
auditors[_auditor].banned = true;
emit BannedAuditor(_auditor);
}
function allowAuditor(address _auditor) external onlyOwner {
require(auditors[_auditor].addr != address(0));
auditors[_auditor].banned = false;
emit AllowedAuditor(_auditor);
}
//=== Audits and Rewards
function createAudit(uint _stake, uint _endTimeInDays, uint _maxAuditors) external payable onlyOwner {
uint endTime = _endTimeInDays * 1 days;
require(endTime < maxAuditDuration);
require(block.timestamp + endTime * 1 days > block.timestamp);
require(msg.value > 0 && _maxAuditors > 0 && _stake > 0);
Audit memory audit;
audit.status = AuditStatus.New;
audit.owner = msg.sender;
audit.id = audits.length;
audit.totalReward = msg.value;
audit.remainingReward = audit.totalReward;
audit.stake = _stake;
audit.endTime = block.timestamp + endTime;
audit.maxAuditors = _maxAuditors;
audits.push(audit); // push into storage
emit CreatedAudit(audit.id);
}
function reviewAudit(uint _id) external onlyOwner {
require(audits[_id].status == AuditStatus.InProgress);
require(block.timestamp >= audits[_id].endTime);
audits[_id].endTime = block.timestamp; // override the endTime to when it actually ended
audits[_id].status = AuditStatus.InReview;
emit ReviewingAudit(_id);
}
function rewardAuditor(uint _id, address _auditor, uint _reward) external onlyOwner {
audits[_id].remainingReward.sub(_reward);
audits[_id].status = AuditStatus.Completed;
auditors[_auditor].totalEarned.add(_reward);
auditors[_auditor].completedAudits.add(1);
auditors[_auditor].canWithdrawStake[_id] = true; // allow them to withdraw their stake after stakePeriod
_auditor.transfer(_reward);
emit AuditorRewarded(_id, _auditor, _reward);
}
function slashStake(uint _id, address _auditor) external onlyOwner {
require(auditors[_auditor].addr != address(0));
require(auditors[_auditor].stakedInAudit[_id]); // participated in audit
auditors[_auditor].canWithdrawStake[_id] = false;
emit SlashedStake(_id, _auditor);
}
//=== User Actions
function stake(uint _id) public payable {
// Check conditions of the Audit
require(msg.value == audits[_id].stake);
require(block.timestamp < audits[_id].endTime);
require(audits[_id].participants.length < audits[_id].maxAuditors);
require(audits[_id].status == AuditStatus.New || audits[_id].status == AuditStatus.InProgress);
// Check conditions of the Auditor
require(auditors[msg.sender].addr == msg.sender && !auditors[msg.sender].banned); // auditor is authorized
require(!auditors[msg.sender].stakedInAudit[_id]); //check if auditor has staked for this audit already
// Update audit's states
audits[_id].status = AuditStatus.InProgress;
audits[_id].participants.push(msg.sender);
// Update auditor's states
auditors[msg.sender].stakedInAudit[_id] = true;
auditors[msg.sender].stakedAudits.push(_id);
emit AuditorStaked(_id, msg.sender, msg.value);
}
function withdrawStake(uint _id) public {
require(audits[_id].status == AuditStatus.Completed);
require(auditors[msg.sender].canWithdrawStake[_id]);
require(block.timestamp >= audits[_id].endTime + stakePeriod);
auditors[msg.sender].canWithdrawStake[_id] = false; //prevent replay attack
address(msg.sender).transfer(audits[_id].stake); // do this last to prevent re-entrancy
emit WithdrawedStake(_id, msg.sender, audits[_id].stake);
}
//=== Getters
function auditorHasStaked(uint _id, address _auditor) public view returns(bool) {
return auditors[_auditor].stakedInAudit[_id];
}
function auditorCanWithdrawStake(uint _id, address _auditor) public view returns(bool) {
if(auditors[_auditor].stakedInAudit[_id] && auditors[_auditor].canWithdrawStake[_id]) {
return true;
}
return false;
}
// return a list of ids that _auditor has staked in
function getStakedAudits(address _auditor) public view returns(uint[]) {
return auditors[_auditor].stakedAudits;
}
// return a list of auditors that participated in this audit
function getAuditors(uint _id) public view returns(address[]) {
return audits[_id].participants;
}
}