pragma solidity ^0.5.17;
// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Uint256Helpers.sol
// Adapted to use pragma ^0.5.17 and satisfy our linter rules
library Uint256Helpers {
uint256 private constant MAX_UINT8 = uint8(-1);
uint256 private constant MAX_UINT64 = uint64(-1);
string private constant ERROR_UINT8_NUMBER_TOO_BIG = "UINT8_NUMBER_TOO_BIG";
string private constant ERROR_UINT64_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG";
function toUint8(uint256 a) internal pure returns (uint8) {
require(a <= MAX_UINT8, ERROR_UINT8_NUMBER_TOO_BIG);
return uint8(a);
}
function toUint64(uint256 a) internal pure returns (uint64) {
require(a <= MAX_UINT64, ERROR_UINT64_NUMBER_TOO_BIG);
return uint64(a);
}
}
/*
* SPDX-License-Identifier: MIT
*/
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address _who) external view returns (uint256);
function allowance(address _owner, address _spender) external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
function approve(address _spender, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
}
/*
* SPDX-License-Identifier: MIT
*/
interface IArbitrator {
/**
* @dev Create a dispute over the Arbitrable sender with a number of possible rulings
* @param _possibleRulings Number of possible rulings allowed for the dispute
* @param _metadata Optional metadata that can be used to provide additional information on the dispute to be created
* @return Dispute identification number
*/
function createDispute(uint256 _possibleRulings, bytes calldata _metadata) external returns (uint256);
/**
* @dev Submit evidence for a dispute
* @param _disputeId Id of the dispute in the Court
* @param _submitter Address of the account submitting the evidence
* @param _evidence Data submitted for the evidence related to the dispute
*/
function submitEvidence(uint256 _disputeId, address _submitter, bytes calldata _evidence) external;
/**
* @dev Close the evidence period of a dispute
* @param _disputeId Identification number of the dispute to close its evidence submitting period
*/
function closeEvidencePeriod(uint256 _disputeId) external;
/**
* @notice Rule dispute #`_disputeId` if ready
* @param _disputeId Identification number of the dispute to be ruled
* @return subject Subject associated to the dispute
* @return ruling Ruling number computed for the given dispute
*/
function rule(uint256 _disputeId) external returns (address subject, uint256 ruling);
/**
* @dev Tell the dispute fees information to create a dispute
* @return recipient Address where the corresponding dispute fees must be transferred to
* @return feeToken ERC20 token used for the fees
* @return feeAmount Total amount of fees that must be allowed to the recipient
*/
function getDisputeFees() external view returns (address recipient, IERC20 feeToken, uint256 feeAmount);
/**
* @dev Tell the payments recipient address
* @return Address of the payments recipient module
*/
function getPaymentsRecipient() external view returns (address);
}
/*
* SPDX-License-Identifier: MIT
*/
/**
* @dev The Arbitrable instances actually don't require to follow any specific interface.
* Note that this is actually optional, although it does allow the Court to at least have a way to identify a specific set of instances.
*/
contract IArbitrable {
/**
* @dev Emitted when an IArbitrable instance's dispute is ruled by an IArbitrator
* @param arbitrator IArbitrator instance ruling the dispute
* @param disputeId Identification number of the dispute being ruled by the arbitrator
* @param ruling Ruling given by the arbitrator
*/
event Ruled(IArbitrator indexed arbitrator, uint256 indexed disputeId, uint256 ruling);
}
// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/IsContract.sol
// Adapted to use pragma ^0.5.17 and satisfy our linter rules
contract IsContract {
/*
* NOTE: this should NEVER be used for authentication
* (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize).
*
* This is only intended to be used as a sanity check that an address is actually a contract,
* RATHER THAN an address not being a contract.
*/
function isContract(address _target) internal view returns (bool) {
if (_target == address(0)) {
return false;
}
uint256 size;
assembly { size := extcodesize(_target) }
return size > 0;
}
}
contract ACL {
string private constant ERROR_BAD_FREEZE = "ACL_BAD_FREEZE";
string private constant ERROR_ROLE_ALREADY_FROZEN = "ACL_ROLE_ALREADY_FROZEN";
string private constant ERROR_INVALID_BULK_INPUT = "ACL_INVALID_BULK_INPUT";
enum BulkOp { Grant, Revoke, Freeze }
address internal constant FREEZE_FLAG = address(1);
address internal constant ANY_ADDR = address(-1);
// List of all roles assigned to different addresses
mapping (bytes32 => mapping (address => bool)) public roles;
event Granted(bytes32 indexed id, address indexed who);
event Revoked(bytes32 indexed id, address indexed who);
event Frozen(bytes32 indexed id);
/**
* @dev Tell whether an address has a role assigned
* @param _who Address being queried
* @param _id ID of the role being checked
* @return True if the requested address has assigned the given role, false otherwise
*/
function hasRole(address _who, bytes32 _id) public view returns (bool) {
return roles[_id][_who] || roles[_id][ANY_ADDR];
}
/**
* @dev Tell whether a role is frozen
* @param _id ID of the role being checked
* @return True if the given role is frozen, false otherwise
*/
function isRoleFrozen(bytes32 _id) public view returns (bool) {
return roles[_id][FREEZE_FLAG];
}
/**
* @dev Internal function to grant a role to a given address
* @param _id ID of the role to be granted
* @param _who Address to grant the role to
*/
function _grant(bytes32 _id, address _who) internal {
require(!isRoleFrozen(_id), ERROR_ROLE_ALREADY_FROZEN);
require(_who != FREEZE_FLAG, ERROR_BAD_FREEZE);
if (!hasRole(_who, _id)) {
roles[_id][_who] = true;
emit Granted(_id, _who);
}
}
/**
* @dev Internal function to revoke a role from a given address
* @param _id ID of the role to be revoked
* @param _who Address to revoke the role from
*/
function _revoke(bytes32 _id, address _who) internal {
require(!isRoleFrozen(_id), ERROR_ROLE_ALREADY_FROZEN);
if (hasRole(_who, _id)) {
roles[_id][_who] = false;
emit Revoked(_id, _who);
}
}
/**
* @dev Internal function to freeze a role
* @param _id ID of the role to be frozen
*/
function _freeze(bytes32 _id) internal {
require(!isRoleFrozen(_id), ERROR_ROLE_ALREADY_FROZEN);
roles[_id][FREEZE_FLAG] = true;
emit Frozen(_id);
}
/**
* @dev Internal function to enact a bulk list of ACL operations
*/
function _bulk(BulkOp[] memory _op, bytes32[] memory _id, address[] memory _who) internal {
require(_op.length == _id.length && _op.length == _who.length, ERROR_INVALID_BULK_INPUT);
for (uint256 i = 0; i < _op.length; i++) {
BulkOp op = _op[i];
if (op == BulkOp.Grant) {
_grant(_id[i], _who[i]);
} else if (op == BulkOp.Revoke) {
_revoke(_id[i], _who[i]);
} else if (op == BulkOp.Freeze) {
_freeze(_id[i]);
}
}
}
}
contract ModuleIds {
// DisputeManager module ID - keccak256(abi.encodePacked("DISPUTE_MANAGER"))
bytes32 internal constant MODULE_ID_DISPUTE_MANAGER = 0x14a6c70f0f6d449c014c7bbc9e68e31e79e8474fb03b7194df83109a2d888ae6;
// GuardiansRegistry module ID - keccak256(abi.encodePacked("GUARDIANS_REGISTRY"))
bytes32 internal constant MODULE_ID_GUARDIANS_REGISTRY = 0x8af7b7118de65da3b974a3fd4b0c702b66442f74b9dff6eaed1037254c0b79fe;
// Voting module ID - keccak256(abi.encodePacked("VOTING"))
bytes32 internal constant MODULE_ID_VOTING = 0x7cbb12e82a6d63ff16fe43977f43e3e2b247ecd4e62c0e340da8800a48c67346;
// PaymentsBook module ID - keccak256(abi.encodePacked("PAYMENTS_BOOK"))
bytes32 internal constant MODULE_ID_PAYMENTS_BOOK = 0xfa275b1417437a2a2ea8e91e9fe73c28eaf0a28532a250541da5ac0d1892b418;
// Treasury module ID - keccak256(abi.encodePacked("TREASURY"))
bytes32 internal constant MODULE_ID_TREASURY = 0x06aa03964db1f7257357ef09714a5f0ca3633723df419e97015e0c7a3e83edb7;
}
interface IModulesLinker {
/**
* @notice Update the implementations of a list of modules
* @param _ids List of IDs of the modules to be updated
* @param _addresses List of module addresses to be updated
*/
function linkModules(bytes32[] calldata _ids, address[] calldata _addresses) external;
}
// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/math/SafeMath64.sol
// Adapted to use pragma ^0.5.17 and satisfy our linter rules
/**
* @title SafeMath64
* @dev Math operations for uint64 with safety checks that revert on error
*/
library SafeMath64 {
string private constant ERROR_ADD_OVERFLOW = "MATH64_ADD_OVERFLOW";
string private constant ERROR_SUB_UNDERFLOW = "MATH64_SUB_UNDERFLOW";
string private constant ERROR_MUL_OVERFLOW = "MATH64_MUL_OVERFLOW";
string private constant ERROR_DIV_ZERO = "MATH64_DIV_ZERO";
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint64 _a, uint64 _b) internal pure returns (uint64) {
uint256 c = uint256(_a) * uint256(_b);
require(c < 0x010000000000000000, ERROR_MUL_OVERFLOW); // 2**64 (less gas this way)
return uint64(c);
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint64 _a, uint64 _b) internal pure returns (uint64) {
require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0
uint64 c = _a / _b;
// assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint64 _a, uint64 _b) internal pure returns (uint64) {
require(_b <= _a, ERROR_SUB_UNDERFLOW);
uint64 c = _a - _b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint64 _a, uint64 _b) internal pure returns (uint64) {
uint64 c = _a + _b;
require(c >= _a, ERROR_ADD_OVERFLOW);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint64 a, uint64 b) internal pure returns (uint64) {
require(b != 0, ERROR_DIV_ZERO);
return a % b;
}
}
// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/TimeHelpers.sol
// Adapted to use pragma ^0.5.17 and satisfy our linter rules
contract TimeHelpers {
using Uint256Helpers for uint256;
/**
* @dev Returns the current block number.
* Using a function rather than `block.number` allows us to easily mock the block number in
* tests.
*/
function getBlockNumber() internal view returns (uint256) {
return block.number;
}
/**
* @dev Returns the current block number, converted to uint64.
* Using a function rather than `block.number` allows us to easily mock the block number in
* tests.
*/
function getBlockNumber64() internal view returns (uint64) {
return getBlockNumber().toUint64();
}
/**
* @dev Returns the current timestamp.
* Using a function rather than `block.timestamp` allows us to easily mock it in
* tests.
*/
function getTimestamp() internal view returns (uint256) {
return block.timestamp; // solium-disable-line security/no-block-members
}
/**
* @dev Returns the current timestamp, converted to uint64.
* Using a function rather than `block.timestamp` allows us to easily mock it in
* tests.
*/
function getTimestamp64() internal view returns (uint64) {
return getTimestamp().toUint64();
}
}
interface IClock {
/**
* @dev Ensure that the current term of the clock is up-to-date
* @return Identification number of the current term
*/
function ensureCurrentTerm() external returns (uint64);
/**
* @dev Transition up to a certain number of terms to leave the clock up-to-date
* @param _maxRequestedTransitions Max number of term transitions allowed by the sender
* @return Identification number of the term ID after executing the heartbeat transitions
*/
function heartbeat(uint64 _maxRequestedTransitions) external returns (uint64);
/**
* @dev Ensure that a certain term has its randomness set
* @return Randomness of the current term
*/
function ensureCurrentTermRandomness() external returns (bytes32);
/**
* @dev Tell the last ensured term identification number
* @return Identification number of the last ensured term
*/
function getLastEnsuredTermId() external view returns (uint64);
/**
* @dev Tell the current term identification number. Note that there may be pending term transitions.
* @return Identification number of the current term
*/
function getCurrentTermId() external view returns (uint64);
/**
* @dev Tell the number of terms the clock should transition to be up-to-date
* @return Number of terms the clock should transition to be up-to-date
*/
function getNeededTermTransitions() external view returns (uint64);
/**
* @dev Tell the information related to a term based on its ID
* @param _termId ID of the term being queried
* @return startTime Term start time
* @return randomnessBN Block number used for randomness in the requested term
* @return randomness Randomness computed for the requested term
*/
function getTerm(uint64 _termId) external view returns (uint64 startTime, uint64 randomnessBN, bytes32 randomness);
/**
* @dev Tell the randomness of a term even if it wasn't computed yet
* @param _termId Identification number of the term being queried
* @return Randomness of the requested term
*/
function getTermRandomness(uint64 _termId) external view returns (bytes32);
}
contract CourtClock is IClock, TimeHelpers {
using SafeMath64 for uint64;
string private constant ERROR_TERM_DOES_NOT_EXIST = "CLK_TERM_DOES_NOT_EXIST";
string private constant ERROR_TERM_DURATION_TOO_LONG = "CLK_TERM_DURATION_TOO_LONG";
string private constant ERROR_TERM_RANDOMNESS_NOT_YET = "CLK_TERM_RANDOMNESS_NOT_YET";
string private constant ERROR_TERM_RANDOMNESS_UNAVAILABLE = "CLK_TERM_RANDOMNESS_UNAVAILABLE";
string private constant ERROR_BAD_FIRST_TERM_START_TIME = "CLK_BAD_FIRST_TERM_START_TIME";
string private constant ERROR_TOO_MANY_TRANSITIONS = "CLK_TOO_MANY_TRANSITIONS";
string private constant ERROR_INVALID_TRANSITION_TERMS = "CLK_INVALID_TRANSITION_TERMS";
string private constant ERROR_CANNOT_DELAY_STARTED_COURT = "CLK_CANNOT_DELAY_STARTED_PROT";
string private constant ERROR_CANNOT_DELAY_PAST_START_TIME = "CLK_CANNOT_DELAY_PAST_START_TIME";
// Maximum number of term transitions a callee may have to assume in order to call certain functions that require the Court being up-to-date
uint64 internal constant MAX_AUTO_TERM_TRANSITIONS_ALLOWED = 1;
// Max duration in seconds that a term can last
uint64 internal constant MAX_TERM_DURATION = 365 days;
// Max time until first term starts since contract is deployed
uint64 internal constant MAX_FIRST_TERM_DELAY_PERIOD = 2 * MAX_TERM_DURATION;
struct Term {
uint64 startTime; // Timestamp when the term started
uint64 randomnessBN; // Block number for entropy
bytes32 randomness; // Entropy from randomnessBN block hash
}
// Duration in seconds for each term of the Court
uint64 private termDuration;
// Last ensured term id
uint64 private termId;
// List of Court terms indexed by id
mapping (uint64 => Term) private terms;
event Heartbeat(uint64 previousTermId, uint64 currentTermId);
event StartTimeDelayed(uint64 previousStartTime, uint64 currentStartTime);
/**
* @dev Ensure a certain term has already been processed
* @param _termId Identification number of the term to be checked
*/
modifier termExists(uint64 _termId) {
require(_termId <= termId, ERROR_TERM_DOES_NOT_EXIST);
_;
}
/**
* @dev Constructor function
* @param _termParams Array containing:
* 0. _termDuration Duration in seconds per term
* 1. _firstTermStartTime Timestamp in seconds when the court will open (to give time for guardian on-boarding)
*/
constructor(uint64[2] memory _termParams) public {
uint64 _termDuration = _termParams[0];
uint64 _firstTermStartTime = _termParams[1];
require(_termDuration < MAX_TERM_DURATION, ERROR_TERM_DURATION_TOO_LONG);
require(_firstTermStartTime >= getTimestamp64() + _termDuration, ERROR_BAD_FIRST_TERM_START_TIME);
require(_firstTermStartTime <= getTimestamp64() + MAX_FIRST_TERM_DELAY_PERIOD, ERROR_BAD_FIRST_TERM_START_TIME);
termDuration = _termDuration;
// No need for SafeMath: we already checked values above
terms[0].startTime = _firstTermStartTime - _termDuration;
}
/**
* @notice Ensure that the current term of the Court is up-to-date. If the Court is outdated by more than `MAX_AUTO_TERM_TRANSITIONS_ALLOWED`
* terms, the heartbeat function must be called manually instead.
* @return Identification number of the current term
*/
function ensureCurrentTerm() external returns (uint64) {
return _ensureCurrentTerm();
}
/**
* @notice Transition up to `_maxRequestedTransitions` terms
* @param _maxRequestedTransitions Max number of term transitions allowed by the sender
* @return Identification number of the term ID after executing the heartbeat transitions
*/
function heartbeat(uint64 _maxRequestedTransitions) external returns (uint64) {
return _heartbeat(_maxRequestedTransitions);
}
/**
* @notice Ensure that a certain term has its randomness set. As we allow to draft disputes requested for previous terms, if there
* were mined more than 256 blocks for the current term, the blockhash of its randomness BN is no longer available, given
* round will be able to be drafted in the following term.
* @return Randomness of the current term
*/
function ensureCurrentTermRandomness() external returns (bytes32) {
// If the randomness for the given term was already computed, return
uint64 currentTermId = termId;
Term storage term = terms[currentTermId];
bytes32 termRandomness = term.randomness;
if (termRandomness != bytes32(0)) {
return termRandomness;
}
// Compute term randomness
bytes32 newRandomness = _computeTermRandomness(currentTermId);
require(newRandomness != bytes32(0), ERROR_TERM_RANDOMNESS_UNAVAILABLE);
term.randomness = newRandomness;
return newRandomness;
}
/**
* @dev Tell the term duration of the Court
* @return Duration in seconds of the Court term
*/
function getTermDuration() external view returns (uint64) {
return termDuration;
}
/**
* @dev Tell the last ensured term identification number
* @return Identification number of the last ensured term
*/
function getLastEnsuredTermId() external view returns (uint64) {
return _lastEnsuredTermId();
}
/**
* @dev Tell the current term identification number. Note that there may be pending term transitions.
* @return Identification number of the current term
*/
function getCurrentTermId() external view returns (uint64) {
return _currentTermId();
}
/**
* @dev Tell the number of terms the Court should transition to be up-to-date
* @return Number of terms the Court should transition to be up-to-date
*/
function getNeededTermTransitions() external view returns (uint64) {
return _neededTermTransitions();
}
/**
* @dev Tell the information related to a term based on its ID. Note that if the term has not been reached, the
* information returned won't be computed yet. This function allows querying future terms that were not computed yet.
* @param _termId ID of the term being queried
* @return startTime Term start time
* @return randomnessBN Block number used for randomness in the requested term
* @return randomness Randomness computed for the requested term
*/
function getTerm(uint64 _termId) external view returns (uint64 startTime, uint64 randomnessBN, bytes32 randomness) {
Term storage term = terms[_termId];
return (term.startTime, term.randomnessBN, term.randomness);
}
/**
* @dev Tell the randomness of a term even if it wasn't computed yet
* @param _termId Identification number of the term being queried
* @return Randomness of the requested term
*/
function getTermRandomness(uint64 _termId) external view termExists(_termId) returns (bytes32) {
return _computeTermRandomness(_termId);
}
/**
* @dev Internal function to ensure that the current term of the Court is up-to-date. If the Court is outdated by more than
* `MAX_AUTO_TERM_TRANSITIONS_ALLOWED` terms, the heartbeat function must be called manually.
* @return Identification number of the resultant term ID after executing the corresponding transitions
*/
function _ensureCurrentTerm() internal returns (uint64) {
// Check the required number of transitions does not exceeds the max allowed number to be processed automatically
uint64 requiredTransitions = _neededTermTransitions();
require(requiredTransitions <= MAX_AUTO_TERM_TRANSITIONS_ALLOWED, ERROR_TOO_MANY_TRANSITIONS);
// If there are no transitions pending, return the last ensured term id
if (uint256(requiredTransitions) == 0) {
return termId;
}
// Process transition if there is at least one pending
return _heartbeat(requiredTransitions);
}
/**
* @dev Internal function to transition the Court terms up to a requested number of terms
* @param _maxRequestedTransitions Max number of term transitions allowed by the sender
* @return Identification number of the resultant term ID after executing the requested transitions
*/
function _heartbeat(uint64 _maxRequestedTransitions) internal returns (uint64) {
// Transition the minimum number of terms between the amount requested and the amount actually needed
uint64 neededTransitions = _neededTermTransitions();
uint256 transitions = uint256(_maxRequestedTransitions < neededTransitions ? _maxRequestedTransitions : neededTransitions);
require(transitions > 0, ERROR_INVALID_TRANSITION_TERMS);
uint64 blockNumber = getBlockNumber64();
uint64 previousTermId = termId;
uint64 currentTermId = previousTermId;
for (uint256 transition = 1; transition <= transitions; transition++) {
// Term IDs are incremented by one based on the number of time periods since the Court started. Since time is represented in uint64,
// even if we chose the minimum duration possible for a term (1 second), we can ensure terms will never reach 2^64 since time is
// already assumed to fit in uint64.
Term storage previousTerm = terms[currentTermId++];
Term storage currentTerm = terms[currentTermId];
_onTermTransitioned(currentTermId);
// Set the start time of the new term. Note that we are using a constant term duration value to guarantee
// equally long terms, regardless of heartbeats.
currentTerm.startTime = previousTerm.startTime.add(termDuration);
// In order to draft a random number of guardians in a term, we use a randomness factor for each term based on a
// block number that is set once the term has started. Note that this information could not be known beforehand.
currentTerm.randomnessBN = blockNumber + 1;
}
termId = currentTermId;
emit Heartbeat(previousTermId, currentTermId);
return currentTermId;
}
/**
* @dev Internal function to delay the first term start time only if it wasn't reached yet
* @param _newFirstTermStartTime New timestamp in seconds when the court will open
*/
function _delayStartTime(uint64 _newFirstTermStartTime) internal {
require(_currentTermId() == 0, ERROR_CANNOT_DELAY_STARTED_COURT);
Term storage term = terms[0];
uint64 currentFirstTermStartTime = term.startTime.add(termDuration);
require(_newFirstTermStartTime > currentFirstTermStartTime, ERROR_CANNOT_DELAY_PAST_START_TIME);
// No need for SafeMath: we already checked above that `_newFirstTermStartTime` > `currentFirstTermStartTime` >= `termDuration`
term.startTime = _newFirstTermStartTime - termDuration;
emit StartTimeDelayed(currentFirstTermStartTime, _newFirstTermStartTime);
}
/**
* @dev Internal function to notify when a term has been transitioned. This function must be overridden to provide custom behavior.
* @param _termId Identification number of the new current term that has been transitioned
*/
function _onTermTransitioned(uint64 _termId) internal;
/**
* @dev Internal function to tell the last ensured term identification number
* @return Identification number of the last ensured term
*/
function _lastEnsuredTermId() internal view returns (uint64) {
return termId;
}
/**
* @dev Internal function to tell the current term identification number. Note that there may be pending term transitions.
* @return Identification number of the current term
*/
function _currentTermId() internal view returns (uint64) {
return termId.add(_neededTermTransitions());
}
/**
* @dev Internal function to tell the number of terms the Court should transition to be up-to-date
* @return Number of terms the Court should transition to be up-to-date
*/
function _neededTermTransitions() internal view returns (uint64) {
// Note that the Court is always initialized providing a start time for the first-term in the future. If that's the case,
// no term transitions are required.
uint64 currentTermStartTime = terms[termId].startTime;
if (getTimestamp64() < currentTermStartTime) {
return uint64(0);
}
// No need for SafeMath: we already know that the start time of the current term is in the past
return (getTimestamp64() - currentTermStartTime) / termDuration;
}
/**
* @dev Internal function to compute the randomness that will be used to draft guardians for the given term. This
* function assumes the given term exists. To determine the randomness factor for a term we use the hash of a
* block number that is set once the term has started to ensure it cannot be known beforehand. Note that the
* hash function being used only works for the 256 most recent block numbers.
* @param _termId Identification number of the term being queried
* @return Randomness computed for the given term
*/
function _computeTermRandomness(uint64 _termId) internal view returns (bytes32) {
Term storage term = terms[_termId];
require(getBlockNumber64() > term.randomnessBN, ERROR_TERM_RANDOMNESS_NOT_YET);
return blockhash(term.randomnessBN);
}
}
// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/math/SafeMath.sol
// Adapted to use pragma ^0.5.17 and satisfy our linter rules
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library SafeMath {
string private constant ERROR_ADD_OVERFLOW = "MATH_ADD_OVERFLOW";
string private constant ERROR_SUB_UNDERFLOW = "MATH_SUB_UNDERFLOW";
string private constant ERROR_MUL_OVERFLOW = "MATH_MUL_OVERFLOW";
string private constant ERROR_DIV_ZERO = "MATH_DIV_ZERO";
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint256 _a, uint256 _b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring '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;
}
uint256 c = _a * _b;
require(c / _a == _b, ERROR_MUL_OVERFLOW);
return c;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts 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 c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
require(_b <= _a, ERROR_SUB_UNDERFLOW);
uint256 c = _a - _b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 _a, uint256 _b) internal pure returns (uint256) {
uint256 c = _a + _b;
require(c >= _a, ERROR_ADD_OVERFLOW);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, ERROR_DIV_ZERO);
return a % b;
}
}
library PctHelpers {
using SafeMath for uint256;
uint256 internal constant PCT_BASE = 10000; // ‱ (1 / 10,000)
function isValid(uint16 _pct) internal pure returns (bool) {
return _pct <= PCT_BASE;
}
function pct(uint256 self, uint16 _pct) internal pure returns (uint256) {
return self.mul(uint256(_pct)) / PCT_BASE;
}
function pct256(uint256 self, uint256 _pct) internal pure returns (uint256) {
return self.mul(_pct) / PCT_BASE;
}
function pctIncrease(uint256 self, uint16 _pct) internal pure returns (uint256) {
// No need for SafeMath: for addition note that `PCT_BASE` is lower than (2^256 - 2^16)
return self.mul(PCT_BASE + uint256(_pct)) / PCT_BASE;
}
}
interface IConfig {
/**
* @dev Tell the full Court configuration parameters at a certain term
* @param _termId Identification number of the term querying the Court config of
* @return token Address of the token used to pay for fees
* @return fees Array containing:
* 0. guardianFee Amount of fee tokens that is paid per guardian per dispute
* 1. draftFee Amount of fee tokens per guardian to cover the drafting cost
* 2. settleFee Amount of fee tokens per guardian to cover round settlement cost
* @return roundStateDurations Array containing the durations in terms of the different phases of a dispute:
* 0. evidenceTerms Max submitting evidence period duration in terms
* 1. commitTerms Commit period duration in terms
* 2. revealTerms Reveal period duration in terms
* 3. appealTerms Appeal period duration in terms
* 4. appealConfirmationTerms Appeal confirmation period duration in terms
* @return pcts Array containing:
* 0. penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
* 1. finalRoundReduction Permyriad of fee reduction for the last appeal round (‱ - 1/10,000)
* @return roundParams Array containing params for rounds:
* 0. firstRoundGuardiansNumber Number of guardians to be drafted for the first round of disputes
* 1. appealStepFactor Increasing factor for the number of guardians of each round of a dispute
* 2. maxRegularAppealRounds Number of regular appeal rounds before the final round is triggered
* @return appealCollateralParams Array containing params for appeal collateral:
* 0. appealCollateralFactor Multiple of dispute fees required to appeal a preliminary ruling
* 1. appealConfirmCollateralFactor Multiple of dispute fees required to confirm appeal
* @return minActiveBalance Minimum amount of tokens guardians have to activate to participate in the Court
*/
function getConfig(uint64 _termId) external view
returns (
IERC20 feeToken,
uint256[3] memory fees,
uint64[5] memory roundStateDurations,
uint16[2] memory pcts,
uint64[4] memory roundParams,
uint256[2] memory appealCollateralParams,
uint256 minActiveBalance
);
/**
* @dev Tell the draft config at a certain term
* @param _termId Identification number of the term querying the draft config of
* @return feeToken Address of the token used to pay for fees
* @return draftFee Amount of fee tokens per guardian to cover the drafting cost
* @return penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
*/
function getDraftConfig(uint64 _termId) external view returns (IERC20 feeToken, uint256 draftFee, uint16 penaltyPct);
/**
* @dev Tell the min active balance config at a certain term
* @param _termId Term querying the min active balance config of
* @return Minimum amount of tokens guardians have to activate to participate in the Court
*/
function getMinActiveBalance(uint64 _termId) external view returns (uint256);
}
contract CourtConfigData {
struct Config {
FeesConfig fees; // Full fees-related config
DisputesConfig disputes; // Full disputes-related config
uint256 minActiveBalance; // Minimum amount of tokens guardians have to activate to participate in the Court
}
struct FeesConfig {
IERC20 token; // ERC20 token to be used for the fees of the Court
uint16 finalRoundReduction; // Permyriad of fees reduction applied for final appeal round (‱ - 1/10,000)
uint256 guardianFee; // Amount of tokens paid to draft a guardian to adjudicate a dispute
uint256 draftFee; // Amount of tokens paid per round to cover the costs of drafting guardians
uint256 settleFee; // Amount of tokens paid per round to cover the costs of slashing guardians
}
struct DisputesConfig {
uint64 evidenceTerms; // Max submitting evidence period duration in terms
uint64 commitTerms; // Committing period duration in terms
uint64 revealTerms; // Revealing period duration in terms
uint64 appealTerms; // Appealing period duration in terms
uint64 appealConfirmTerms; // Confirmation appeal period duration in terms
uint16 penaltyPct; // Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
uint64 firstRoundGuardiansNumber; // Number of guardians drafted on first round
uint64 appealStepFactor; // Factor in which the guardians number is increased on each appeal
uint64 finalRoundLockTerms; // Period a coherent guardian in the final round will remain locked
uint256 maxRegularAppealRounds; // Before the final appeal
uint256 appealCollateralFactor; // Permyriad multiple of dispute fees required to appeal a preliminary ruling (‱ - 1/10,000)
uint256 appealConfirmCollateralFactor; // Permyriad multiple of dispute fees required to confirm appeal (‱ - 1/10,000)
}
struct DraftConfig {
IERC20 feeToken; // ERC20 token to be used for the fees of the Court
uint16 penaltyPct; // Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
uint256 draftFee; // Amount of tokens paid per round to cover the costs of drafting guardians
}
}
contract CourtConfig is IConfig, CourtConfigData {
using SafeMath64 for uint64;
using PctHelpers for uint256;
string private constant ERROR_TOO_OLD_TERM = "CONF_TOO_OLD_TERM";
string private constant ERROR_INVALID_PENALTY_PCT = "CONF_INVALID_PENALTY_PCT";
string private constant ERROR_INVALID_FINAL_ROUND_REDUCTION_PCT = "CONF_INVALID_FINAL_ROUND_RED_PCT";
string private constant ERROR_INVALID_MAX_APPEAL_ROUNDS = "CONF_INVALID_MAX_APPEAL_ROUNDS";
string private constant ERROR_LARGE_ROUND_PHASE_DURATION = "CONF_LARGE_ROUND_PHASE_DURATION";
string private constant ERROR_BAD_INITIAL_GUARDIANS_NUMBER = "CONF_BAD_INITIAL_GUARDIAN_NUMBER";
string private constant ERROR_BAD_APPEAL_STEP_FACTOR = "CONF_BAD_APPEAL_STEP_FACTOR";
string private constant ERROR_ZERO_COLLATERAL_FACTOR = "CONF_ZERO_COLLATERAL_FACTOR";
string private constant ERROR_ZERO_MIN_ACTIVE_BALANCE = "CONF_ZERO_MIN_ACTIVE_BALANCE";
// Max number of terms that each of the different adjudication states can last (if lasted 1h, this would be a year)
uint64 internal constant MAX_ADJ_STATE_DURATION = 8670;
// Cap the max number of regular appeal rounds
uint256 internal constant MAX_REGULAR_APPEAL_ROUNDS_LIMIT = 10;
// Future term ID in which a config change has been scheduled
uint64 private configChangeTermId;
// List of all the configs used in the Court
Config[] private configs;
// List of configs indexed by id
mapping (uint64 => uint256) private configIdByTerm;
event NewConfig(uint64 fromTermId, uint64 courtConfigId);
/**
* @dev Constructor function
* @param _feeToken Address of the token contract that is used to pay for fees
* @param _fees Array containing:
* 0. guardianFee Amount of fee tokens that is paid per guardian per dispute
* 1. draftFee Amount of fee tokens per guardian to cover the drafting cost
* 2. settleFee Amount of fee tokens per guardian to cover round settlement cost
* @param _roundStateDurations Array containing the durations in terms of the different phases of a dispute:
* 0. evidenceTerms Max submitting evidence period duration in terms
* 1. commitTerms Commit period duration in terms
* 2. revealTerms Reveal period duration in terms
* 3. appealTerms Appeal period duration in terms
* 4. appealConfirmationTerms Appeal confirmation period duration in terms
* @param _pcts Array containing:
* 0. penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
* 1. finalRoundReduction Permyriad of fee reduction for the last appeal round (‱ - 1/10,000)
* @param _roundParams Array containing params for rounds:
* 0. firstRoundGuardiansNumber Number of guardians to be drafted for the first round of disputes
* 1. appealStepFactor Increasing factor for the number of guardians of each round of a dispute
* 2. maxRegularAppealRounds Number of regular appeal rounds before the final round is triggered
* 3. finalRoundLockTerms Number of terms that a coherent guardian in a final round is disallowed to withdraw (to prevent 51% attacks)
* @param _appealCollateralParams Array containing params for appeal collateral:
* 0. appealCollateralFactor Multiple of dispute fees required to appeal a preliminary ruling
* 1. appealConfirmCollateralFactor Multiple of dispute fees required to confirm appeal
* @param _minActiveBalance Minimum amount of guardian tokens that can be activated
*/
constructor(
IERC20 _feeToken,
uint256[3] memory _fees,
uint64[5] memory _roundStateDurations,
uint16[2] memory _pcts,
uint64[4] memory _roundParams,
uint256[2] memory _appealCollateralParams,
uint256 _minActiveBalance
)
public
{
// Leave config at index 0 empty for non-scheduled config changes
configs.length = 1;
_setConfig(
0,
0,
_feeToken,
_fees,
_roundStateDurations,
_pcts,
_roundParams,
_appealCollateralParams,
_minActiveBalance
);
}
/**
* @dev Tell the full Court configuration parameters at a certain term
* @param _termId Identification number of the term querying the Court config of
* @return token Address of the token used to pay for fees
* @return fees Array containing:
* 0. guardianFee Amount of fee tokens that is paid per guardian per dispute
* 1. draftFee Amount of fee tokens per guardian to cover the drafting cost
* 2. settleFee Amount of fee tokens per guardian to cover round settlement cost
* @return roundStateDurations Array containing the durations in terms of the different phases of a dispute:
* 0. evidenceTerms Max submitting evidence period duration in terms
* 1. commitTerms Commit period duration in terms
* 2. revealTerms Reveal period duration in terms
* 3. appealTerms Appeal period duration in terms
* 4. appealConfirmationTerms Appeal confirmation period duration in terms
* @return pcts Array containing:
* 0. penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
* 1. finalRoundReduction Permyriad of fee reduction for the last appeal round (‱ - 1/10,000)
* @return roundParams Array containing params for rounds:
* 0. firstRoundGuardiansNumber Number of guardians to be drafted for the first round of disputes
* 1. appealStepFactor Increasing factor for the number of guardians of each round of a dispute
* 2. maxRegularAppealRounds Number of regular appeal rounds before the final round is triggered
* @return appealCollateralParams Array containing params for appeal collateral:
* 0. appealCollateralFactor Multiple of dispute fees required to appeal a preliminary ruling
* 1. appealConfirmCollateralFactor Multiple of dispute fees required to confirm appeal
* @return minActiveBalance Minimum amount of tokens guardians have to activate to participate in the Court
*/
function getConfig(uint64 _termId) external view
returns (
IERC20 feeToken,
uint256[3] memory fees,
uint64[5] memory roundStateDurations,
uint16[2] memory pcts,
uint64[4] memory roundParams,
uint256[2] memory appealCollateralParams,
uint256 minActiveBalance
);
/**
* @dev Tell the draft config at a certain term
* @param _termId Identification number of the term querying the draft config of
* @return feeToken Address of the token used to pay for fees
* @return draftFee Amount of fee tokens per guardian to cover the drafting cost
* @return penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
*/
function getDraftConfig(uint64 _termId) external view returns (IERC20 feeToken, uint256 draftFee, uint16 penaltyPct);
/**
* @dev Tell the min active balance config at a certain term
* @param _termId Term querying the min active balance config of
* @return Minimum amount of tokens guardians have to activate to participate in the Court
*/
function getMinActiveBalance(uint64 _termId) external view returns (uint256);
/**
* @dev Tell the term identification number of the next scheduled config change
* @return Term identification number of the next scheduled config change
*/
function getConfigChangeTermId() external view returns (uint64) {
return configChangeTermId;
}
/**
* @dev Internal to make sure to set a config for the new term, it will copy the previous term config if none
* @param _termId Identification number of the new current term that has been transitioned
*/
function _ensureTermConfig(uint64 _termId) internal {
// If the term being transitioned had no config change scheduled, keep the previous one
uint256 currentConfigId = configIdByTerm[_termId];
if (currentConfigId == 0) {
uint256 previousConfigId = configIdByTerm[_termId.sub(1)];
configIdByTerm[_termId] = previousConfigId;
}
}
/**
* @dev Assumes that sender it's allowed (either it's from governor or it's on init)
* @param _termId Identification number of the current Court term
* @param _fromTermId Identification number of the term in which the config will be effective at
* @param _feeToken Address of the token contract that is used to pay for fees.
* @param _fees Array containing:
* 0. guardianFee Amount of fee tokens that is paid per guardian per dispute
* 1. draftFee Amount of fee tokens per guardian to cover the drafting cost
* 2. settleFee Amount of fee tokens per guardian to cover round settlement cost
* @param _roundStateDurations Array containing the durations in terms of the different phases of a dispute:
* 0. evidenceTerms Max submitting evidence period duration in terms
* 1. commitTerms Commit period duration in terms
* 2. revealTerms Reveal period duration in terms
* 3. appealTerms Appeal period duration in terms
* 4. appealConfirmationTerms Appeal confirmation period duration in terms
* @param _pcts Array containing:
* 0. penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
* 1. finalRoundReduction Permyriad of fee reduction for the last appeal round (‱ - 1/10,000)
* @param _roundParams Array containing params for rounds:
* 0. firstRoundGuardiansNumber Number of guardians to be drafted for the first round of disputes
* 1. appealStepFactor Increasing factor for the number of guardians of each round of a dispute
* 2. maxRegularAppealRounds Number of regular appeal rounds before the final round is triggered
* 3. finalRoundLockTerms Number of terms that a coherent guardian in a final round is disallowed to withdraw (to prevent 51% attacks)
* @param _appealCollateralParams Array containing params for appeal collateral:
* 0. appealCollateralFactor Multiple of dispute fees required to appeal a preliminary ruling
* 1. appealConfirmCollateralFactor Multiple of dispute fees required to confirm appeal
* @param _minActiveBalance Minimum amount of guardian tokens that can be activated
*/
function _setConfig(
uint64 _termId,
uint64 _fromTermId,
IERC20 _feeToken,
uint256[3] memory _fees,
uint64[5] memory _roundStateDurations,
uint16[2] memory _pcts,
uint64[4] memory _roundParams,
uint256[2] memory _appealCollateralParams,
uint256 _minActiveBalance
)
internal
{
// If the current term is not zero, changes must be scheduled at least after the current period.
// No need to ensure delays for on-going disputes since these already use their creation term for that.
require(_termId == 0 || _fromTermId > _termId, ERROR_TOO_OLD_TERM);
// Make sure appeal collateral factors are greater than zero
require(_appealCollateralParams[0] > 0 && _appealCollateralParams[1] > 0, ERROR_ZERO_COLLATERAL_FACTOR);
// Make sure the given penalty and final round reduction pcts are not greater than 100%
require(PctHelpers.isValid(_pcts[0]), ERROR_INVALID_PENALTY_PCT);
require(PctHelpers.isValid(_pcts[1]), ERROR_INVALID_FINAL_ROUND_REDUCTION_PCT);
// Disputes must request at least one guardian to be drafted initially
require(_roundParams[0] > 0, ERROR_BAD_INITIAL_GUARDIANS_NUMBER);
// Prevent that further rounds have zero guardians
require(_roundParams[1] > 0, ERROR_BAD_APPEAL_STEP_FACTOR);
// Make sure the max number of appeals allowed does not reach the limit
uint256 _maxRegularAppealRounds = _roundParams[2];
bool isMaxAppealRoundsValid = _maxRegularAppealRounds > 0 && _maxRegularAppealRounds <= MAX_REGULAR_APPEAL_ROUNDS_LIMIT;
require(isMaxAppealRoundsValid, ERROR_INVALID_MAX_APPEAL_ROUNDS);
// Make sure each adjudication round phase duration is valid
for (uint i = 0; i < _roundStateDurations.length; i++) {
require(_roundStateDurations[i] > 0 && _roundStateDurations[i] < MAX_ADJ_STATE_DURATION, ERROR_LARGE_ROUND_PHASE_DURATION);
}
// Make sure min active balance is not zero
require(_minActiveBalance > 0, ERROR_ZERO_MIN_ACTIVE_BALANCE);
// If there was a config change already scheduled, reset it (in that case we will overwrite last array item).
// Otherwise, schedule a new config.
if (configChangeTermId > _termId) {
configIdByTerm[configChangeTermId] = 0;
} else {
configs.length++;
}
uint64 courtConfigId = uint64(configs.length - 1);
Config storage config = configs[courtConfigId];
config.fees = FeesConfig({
token: _feeToken,
guardianFee: _fees[0],
draftFee: _fees[1],
settleFee: _fees[2],
finalRoundReduction: _pcts[1]
});
config.disputes = DisputesConfig({
evidenceTerms: _roundStateDurations[0],
commitTerms: _roundStateDurations[1],
revealTerms: _roundStateDurations[2],
appealTerms: _roundStateDurations[3],
appealConfirmTerms: _roundStateDurations[4],
penaltyPct: _pcts[0],
firstRoundGuardiansNumber: _roundParams[0],
appealStepFactor: _roundParams[1],
maxRegularAppealRounds: _maxRegularAppealRounds,
finalRoundLockTerms: _roundParams[3],
appealCollateralFactor: _appealCollateralParams[0],
appealConfirmCollateralFactor: _appealCollateralParams[1]
});
config.minActiveBalance = _minActiveBalance;
configIdByTerm[_fromTermId] = courtConfigId;
configChangeTermId = _fromTermId;
emit NewConfig(_fromTermId, courtConfigId);
}
/**
* @dev Internal function to get the Court config for a given term
* @param _termId Identification number of the term querying the Court config of
* @param _lastEnsuredTermId Identification number of the last ensured term of the Court
* @return token Address of the token used to pay for fees
* @return fees Array containing:
* 0. guardianFee Amount of fee tokens that is paid per guardian per dispute
* 1. draftFee Amount of fee tokens per guardian to cover the drafting cost
* 2. settleFee Amount of fee tokens per guardian to cover round settlement cost
* @return roundStateDurations Array containing the durations in terms of the different phases of a dispute:
* 0. evidenceTerms Max submitting evidence period duration in terms
* 1. commitTerms Commit period duration in terms
* 2. revealTerms Reveal period duration in terms
* 3. appealTerms Appeal period duration in terms
* 4. appealConfirmationTerms Appeal confirmation period duration in terms
* @return pcts Array containing:
* 0. penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
* 1. finalRoundReduction Permyriad of fee reduction for the last appeal round (‱ - 1/10,000)
* @return roundParams Array containing params for rounds:
* 0. firstRoundGuardiansNumber Number of guardians to be drafted for the first round of disputes
* 1. appealStepFactor Increasing factor for the number of guardians of each round of a dispute
* 2. maxRegularAppealRounds Number of regular appeal rounds before the final round is triggered
* 3. finalRoundLockTerms Number of terms that a coherent guardian in a final round is disallowed to withdraw (to prevent 51% attacks)
* @return appealCollateralParams Array containing params for appeal collateral:
* 0. appealCollateralFactor Multiple of dispute fees required to appeal a preliminary ruling
* 1. appealConfirmCollateralFactor Multiple of dispute fees required to confirm appeal
* @return minActiveBalance Minimum amount of guardian tokens that can be activated
*/
function _getConfigAt(uint64 _termId, uint64 _lastEnsuredTermId) internal view
returns (
IERC20 feeToken,
uint256[3] memory fees,
uint64[5] memory roundStateDurations,
uint16[2] memory pcts,
uint64[4] memory roundParams,
uint256[2] memory appealCollateralParams,
uint256 minActiveBalance
)
{
Config storage config = _getConfigFor(_termId, _lastEnsuredTermId);
FeesConfig storage feesConfig = config.fees;
feeToken = feesConfig.token;
fees = [feesConfig.guardianFee, feesConfig.draftFee, feesConfig.settleFee];
DisputesConfig storage disputesConfig = config.disputes;
roundStateDurations = [
disputesConfig.evidenceTerms,
disputesConfig.commitTerms,
disputesConfig.revealTerms,
disputesConfig.appealTerms,
disputesConfig.appealConfirmTerms
];
pcts = [disputesConfig.penaltyPct, feesConfig.finalRoundReduction];
roundParams = [
disputesConfig.firstRoundGuardiansNumber,
disputesConfig.appealStepFactor,
uint64(disputesConfig.maxRegularAppealRounds),
disputesConfig.finalRoundLockTerms
];
appealCollateralParams = [disputesConfig.appealCollateralFactor, disputesConfig.appealConfirmCollateralFactor];
minActiveBalance = config.minActiveBalance;
}
/**
* @dev Tell the draft config at a certain term
* @param _termId Identification number of the term querying the draft config of
* @param _lastEnsuredTermId Identification number of the last ensured term of the Court
* @return feeToken Address of the token used to pay for fees
* @return draftFee Amount of fee tokens per guardian to cover the drafting cost
* @return penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
*/
function _getDraftConfig(uint64 _termId, uint64 _lastEnsuredTermId) internal view
returns (IERC20 feeToken, uint256 draftFee, uint16 penaltyPct)
{
Config storage config = _getConfigFor(_termId, _lastEnsuredTermId);
return (config.fees.token, config.fees.draftFee, config.disputes.penaltyPct);
}
/**
* @dev Internal function to get the min active balance config for a given term
* @param _termId Identification number of the term querying the min active balance config of
* @param _lastEnsuredTermId Identification number of the last ensured term of the Court
* @return Minimum amount of guardian tokens that can be activated at the given term
*/
function _getMinActiveBalance(uint64 _termId, uint64 _lastEnsuredTermId) internal view returns (uint256) {
Config storage config = _getConfigFor(_termId, _lastEnsuredTermId);
return config.minActiveBalance;
}
/**
* @dev Internal function to get the Court config for a given term
* @param _termId Identification number of the term querying the min active balance config of
* @param _lastEnsuredTermId Identification number of the last ensured term of the Court
* @return Court config for the given term
*/
function _getConfigFor(uint64 _termId, uint64 _lastEnsuredTermId) internal view returns (Config storage) {
uint256 id = _getConfigIdFor(_termId, _lastEnsuredTermId);
return configs[id];
}
/**
* @dev Internal function to get the Court config ID for a given term
* @param _termId Identification number of the term querying the Court config of
* @param _lastEnsuredTermId Identification number of the last ensured term of the Court
* @return Identification number of the config for the given terms
*/
function _getConfigIdFor(uint64 _termId, uint64 _lastEnsuredTermId) internal view returns (uint256) {
// If the given term is lower or equal to the last ensured Court term, it is safe to use a past Court config
if (_termId <= _lastEnsuredTermId) {
return configIdByTerm[_termId];
}
// If the given term is in the future but there is a config change scheduled before it, use the incoming config
uint64 scheduledChangeTermId = configChangeTermId;
if (scheduledChangeTermId <= _termId) {
return configIdByTerm[scheduledChangeTermId];
}
// If no changes are scheduled, use the Court config of the last ensured term
return configIdByTerm[_lastEnsuredTermId];
}
}
interface IDisputeManager {
enum DisputeState {
PreDraft,
Adjudicating,
Ruled
}
enum AdjudicationState {
Invalid,
Committing,
Revealing,
Appealing,
ConfirmingAppeal,
Ended
}
/**
* @dev Create a dispute to be drafted in a future term
* @param _subject Arbitrable instance creating the dispute
* @param _possibleRulings Number of possible rulings allowed for the drafted guardians to vote on the dispute
* @param _metadata Optional metadata that can be used to provide additional information on the dispute to be created
* @return Dispute identification number
*/
function createDispute(IArbitrable _subject, uint8 _possibleRulings, bytes calldata _metadata) external returns (uint256);
/**
* @dev Submit evidence for a dispute
* @param _subject Arbitrable instance submitting the dispute
* @param _disputeId Identification number of the dispute receiving new evidence
* @param _submitter Address of the account submitting the evidence
* @param _evidence Data submitted for the evidence of the dispute
*/
function submitEvidence(IArbitrable _subject, uint256 _disputeId, address _submitter, bytes calldata _evidence) external;
/**
* @dev Close the evidence period of a dispute
* @param _subject IArbitrable instance requesting to close the evidence submission period
* @param _disputeId Identification number of the dispute to close its evidence submitting period
*/
function closeEvidencePeriod(IArbitrable _subject, uint256 _disputeId) external;
/**
* @dev Draft guardians for the next round of a dispute
* @param _disputeId Identification number of the dispute to be drafted
*/
function draft(uint256 _disputeId) external;
/**
* @dev Appeal round of a dispute in favor of a certain ruling
* @param _disputeId Identification number of the dispute being appealed
* @param _roundId Identification number of the dispute round being appealed
* @param _ruling Ruling appealing a dispute round in favor of
*/
function createAppeal(uint256 _disputeId, uint256 _roundId, uint8 _ruling) external;
/**
* @dev Confirm appeal for a round of a dispute in favor of a ruling
* @param _disputeId Identification number of the dispute confirming an appeal of
* @param _roundId Identification number of the dispute round confirming an appeal of
* @param _ruling Ruling being confirmed against a dispute round appeal
*/
function confirmAppeal(uint256 _disputeId, uint256 _roundId, uint8 _ruling) external;
/**
* @dev Compute the final ruling for a dispute
* @param _disputeId Identification number of the dispute to compute its final ruling
* @return subject Arbitrable instance associated to the dispute
* @return finalRuling Final ruling decided for the given dispute
*/
function computeRuling(uint256 _disputeId) external returns (IArbitrable subject, uint8 finalRuling);
/**
* @dev Settle penalties for a round of a dispute
* @param _disputeId Identification number of the dispute to settle penalties for
* @param _roundId Identification number of the dispute round to settle penalties for
* @param _guardiansToSettle Maximum number of guardians to be slashed in this call
*/
function settlePenalties(uint256 _disputeId, uint256 _roundId, uint256 _guardiansToSettle) external;
/**
* @dev Claim rewards for a round of a dispute for guardian
* @dev For regular rounds, it will only reward winning guardians
* @param _disputeId Identification number of the dispute to settle rewards for
* @param _roundId Identification number of the dispute round to settle rewards for
* @param _guardian Address of the guardian to settle their rewards
*/
function settleReward(uint256 _disputeId, uint256 _roundId, address _guardian) external;
/**
* @dev Settle appeal deposits for a round of a dispute
* @param _disputeId Identification number of the dispute to settle appeal deposits for
* @param _roundId Identification number of the dispute round to settle appeal deposits for
*/
function settleAppealDeposit(uint256 _disputeId, uint256 _roundId) external;
/**
* @dev Tell the amount of token fees required to create a dispute
* @return feeToken ERC20 token used for the fees
* @return feeAmount Total amount of fees to be paid for a dispute at the given term
*/
function getDisputeFees() external view returns (IERC20 feeToken, uint256 feeAmount);
/**
* @dev Tell information of a certain dispute
* @param _disputeId Identification number of the dispute being queried
* @return subject Arbitrable subject being disputed
* @return possibleRulings Number of possible rulings allowed for the drafted guardians to vote on the dispute
* @return state Current state of the dispute being queried: pre-draft, adjudicating, or ruled
* @return finalRuling The winning ruling in case the dispute is finished
* @return lastRoundId Identification number of the last round created for the dispute
* @return createTermId Identification number of the term when the dispute was created
*/
function getDispute(uint256 _disputeId) external view
returns (IArbitrable subject, uint8 possibleRulings, DisputeState state, uint8 finalRuling, uint256 lastRoundId, uint64 createTermId);
/**
* @dev Tell information of a certain adjudication round
* @param _disputeId Identification number of the dispute being queried
* @param _roundId Identification number of the round being queried
* @return draftTerm Term from which the requested round can be drafted
* @return delayedTerms Number of terms the given round was delayed based on its requested draft term id
* @return guardiansNumber Number of guardians requested for the round
* @return selectedGuardians Number of guardians already selected for the requested round
* @return settledPenalties Whether or not penalties have been settled for the requested round
* @return collectedTokens Amount of guardian tokens that were collected from slashed guardians for the requested round
* @return coherentGuardians Number of guardians that voted in favor of the final ruling in the requested round
* @return state Adjudication state of the requested round
*/
function getRound(uint256 _disputeId, uint256 _roundId) external view
returns (
uint64 draftTerm,
uint64 delayedTerms,
uint64 guardiansNumber,
uint64 selectedGuardians,
uint256 guardianFees,
bool settledPenalties,
uint256 collectedTokens,
uint64 coherentGuardians,
AdjudicationState state
);
/**
* @dev Tell appeal-related information of a certain adjudication round
* @param _disputeId Identification number of the dispute being queried
* @param _roundId Identification number of the round being queried
* @return maker Address of the account appealing the given round
* @return appealedRuling Ruling confirmed by the appealer of the given round
* @return taker Address of the account confirming the appeal of the given round
* @return opposedRuling Ruling confirmed by the appeal taker of the given round
*/
function getAppeal(uint256 _disputeId, uint256 _roundId) external view
returns (address maker, uint64 appealedRuling, address taker, uint64 opposedRuling);
/**
* @dev Tell information related to the next round due to an appeal of a certain round given.
* @param _disputeId Identification number of the dispute being queried
* @param _roundId Identification number of the round requesting the appeal details of
* @return nextRoundStartTerm Term ID from which the next round will start
* @return nextRoundGuardiansNumber Guardians number for the next round
* @return newDisputeState New state for the dispute associated to the given round after the appeal
* @return feeToken ERC20 token used for the next round fees
* @return guardianFees Total amount of fees to be distributed between the winning guardians of the next round
* @return totalFees Total amount of fees for a regular round at the given term
* @return appealDeposit Amount to be deposit of fees for a regular round at the given term
* @return confirmAppealDeposit Total amount of fees for a regular round at the given term
*/
function getNextRoundDetails(uint256 _disputeId, uint256 _roundId) external view
returns (
uint64 nextRoundStartTerm,
uint64 nextRoundGuardiansNumber,
DisputeState newDisputeState,
IERC20 feeToken,
uint256 totalFees,
uint256 guardianFees,
uint256 appealDeposit,
uint256 confirmAppealDeposit
);
/**
* @dev Tell guardian-related information of a certain adjudication round
* @param _disputeId Identification number of the dispute being queried
* @param _roundId Identification number of the round being queried
* @param _guardian Address of the guardian being queried
* @return weight Guardian weight drafted for the requested round
* @return rewarded Whether or not the given guardian was rewarded based on the requested round
*/
function getGuardian(uint256 _disputeId, uint256 _roundId, address _guardian) external view returns (uint64 weight, bool rewarded);
}
contract Controller is IsContract, ModuleIds, CourtClock, CourtConfig, ACL {
string private constant ERROR_SENDER_NOT_GOVERNOR = "CTR_SENDER_NOT_GOVERNOR";
string private constant ERROR_INVALID_GOVERNOR_ADDRESS = "CTR_INVALID_GOVERNOR_ADDRESS";
string private constant ERROR_MODULE_NOT_SET = "CTR_MODULE_NOT_SET";
string private constant ERROR_MODULE_ALREADY_ENABLED = "CTR_MODULE_ALREADY_ENABLED";
string private constant ERROR_MODULE_ALREADY_DISABLED = "CTR_MODULE_ALREADY_DISABLED";
string private constant ERROR_DISPUTE_MANAGER_NOT_ACTIVE = "CTR_DISPUTE_MANAGER_NOT_ACTIVE";
string private constant ERROR_CUSTOM_FUNCTION_NOT_SET = "CTR_CUSTOM_FUNCTION_NOT_SET";
string private constant ERROR_IMPLEMENTATION_NOT_CONTRACT = "CTR_IMPLEMENTATION_NOT_CONTRACT";
string private constant ERROR_INVALID_IMPLS_INPUT_LENGTH = "CTR_INVALID_IMPLS_INPUT_LENGTH";
address private constant ZERO_ADDRESS = address(0);
/**
* @dev Governor of the whole system. Set of three addresses to recover funds, change configuration settings and setup modules
*/
struct Governor {
address funds; // This address can be unset at any time. It is allowed to recover funds from the ControlledRecoverable modules
address config; // This address is meant not to be unset. It is allowed to change the different configurations of the whole system
address modules; // This address can be unset at any time. It is allowed to plug/unplug modules from the system
}
/**
* @dev Module information
*/
struct Module {
bytes32 id; // ID associated to a module
bool disabled; // Whether the module is disabled
}
// Governor addresses of the system
Governor private governor;
// List of current modules registered for the system indexed by ID
mapping (bytes32 => address) internal currentModules;
// List of all historical modules registered for the system indexed by address
mapping (address => Module) internal allModules;
// List of custom function targets indexed by signature
mapping (bytes4 => address) internal customFunctions;
event ModuleSet(bytes32 id, address addr);
event ModuleEnabled(bytes32 id, address addr);
event ModuleDisabled(bytes32 id, address addr);
event CustomFunctionSet(bytes4 signature, address target);
event FundsGovernorChanged(address previousGovernor, address currentGovernor);
event ConfigGovernorChanged(address previousGovernor, address currentGovernor);
event ModulesGovernorChanged(address previousGovernor, address currentGovernor);
/**
* @dev Ensure the msg.sender is the funds governor
*/
modifier onlyFundsGovernor {
require(msg.sender == governor.funds, ERROR_SENDER_NOT_GOVERNOR);
_;
}
/**
* @dev Ensure the msg.sender is the modules governor
*/
modifier onlyConfigGovernor {
require(msg.sender == governor.config, ERROR_SENDER_NOT_GOVERNOR);
_;
}
/**
* @dev Ensure the msg.sender is the modules governor
*/
modifier onlyModulesGovernor {
require(msg.sender == governor.modules, ERROR_SENDER_NOT_GOVERNOR);
_;
}
/**
* @dev Ensure the given dispute manager is active
*/
modifier onlyActiveDisputeManager(IDisputeManager _disputeManager) {
require(!_isModuleDisabled(address(_disputeManager)), ERROR_DISPUTE_MANAGER_NOT_ACTIVE);
_;
}
/**
* @dev Constructor function
* @param _termParams Array containing:
* 0. _termDuration Duration in seconds per term
* 1. _firstTermStartTime Timestamp in seconds when the court will open (to give time for guardian on-boarding)
* @param _governors Array containing:
* 0. _fundsGovernor Address of the funds governor
* 1. _configGovernor Address of the config governor
* 2. _modulesGovernor Address of the modules governor
* @param _feeToken Address of the token contract that is used to pay for fees
* @param _fees Array containing:
* 0. guardianFee Amount of fee tokens that is paid per guardian per dispute
* 1. draftFee Amount of fee tokens per guardian to cover the drafting cost
* 2. settleFee Amount of fee tokens per guardian to cover round settlement cost
* @param _roundStateDurations Array containing the durations in terms of the different phases of a dispute:
* 0. evidenceTerms Max submitting evidence period duration in terms
* 1. commitTerms Commit period duration in terms
* 2. revealTerms Reveal period duration in terms
* 3. appealTerms Appeal period duration in terms
* 4. appealConfirmationTerms Appeal confirmation period duration in terms
* @param _pcts Array containing:
* 0. penaltyPct Permyriad of min active tokens balance to be locked to each drafted guardians (‱ - 1/10,000)
* 1. finalRoundReduction Permyriad of fee reduction for the last appeal round (‱ - 1/10,000)
* @param _roundParams Array containing params for rounds:
* 0. firstRoundGuardiansNumber Number of guardians to be drafted for the first round of disputes
* 1. appealStepFactor Increasing factor for the number of guardians of each round of a dispute
* 2. maxRegularAppealRounds Number of regular appeal rounds before the final round is triggered
* 3. finalRoundLockTerms Number of terms that a coherent guardian in a final round is disallowed to withdraw (to prevent 51% attacks)
* @param _appealCollateralParams Array containing params for appeal collateral:
* 1. appealCollateralFactor Permyriad multiple of dispute fees required to appeal a preliminary ruling
* 2. appealConfirmCollateralFactor Permyriad multiple of dispute fees required to confirm appeal
* @param _minActiveBalance Minimum amount of guardian tokens that can be activated
*/
constructor(
uint64[2] memory _termParams,
address[3] memory _governors,
IERC20 _feeToken,
uint256[3] memory _fees,
uint64[5] memory _roundStateDurations,
uint16[2] memory _pcts,
uint64[4] memory _roundParams,
uint256[2] memory _appealCollateralParams,
uint256 _minActiveBalance
)
public
CourtClock(_termParams)
CourtConfig(_feeToken, _fees, _roundStateDurations, _pcts, _roundParams, _appealCollateralParams, _minActiveBalance)
{
_setFundsGovernor(_governors[0]);
_setConfigGovernor(_governors[1]);
_setModulesGovernor(_governors[2]);
}
/**
* @dev Fallback function allows to forward calls to a specific address in case it was previously registered
* Note the sender will be always the controller in case it is forwarded
*/
function () external payable {
address target = customFunctions[msg.sig];
require(target != address(0), ERROR_CUSTOM_FUNCTION_NOT_SET);
// solium-disable-next-line security/no-call-value
(bool success,) = address(target).call.value(msg.value)(msg.data);
assembly {
let size := returndatasize
let ptr := mload(0x40)
returndatacopy(ptr, 0, size)
let result := success
switch result case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
/**
* @notice Change Court configuration params
* @param _fromTermId Identification number of the term in which the config will be effective at
* @param _feeToken Address of the token contract that is used to pay for fees
* @param _fees Array containing:
* 0. guardianFee Amount of fee tokens that is paid per guardian per dispute
* 1. draftFee Amount of fee tokens per guardian to cover the drafting cost
* 2. settleFee Amount of fee tokens per guardian to cover round settlement cost
* @param _roundStateDurations Array containing the durations in terms of the different phases of a dispute:
* 0. evidenceTerms Max submitting evidence period duration in terms
* 1. commitTerms Commit period duration in terms
* 2. revealTerms Reveal period duration in terms
* 3. appealTerms Appeal period duration in terms
* 4. appealConfirmationTerms Appeal confirmation period duration in terms
* @param _pcts Array containing:
* 0. penaltyPct Permyriad of min active tokens balance to be locked to each drafted guardians (‱ - 1/10,000)
* 1. finalRoundReduction Permyriad of fee reduction for the last appeal round (‱ - 1/10,000)
* @param _roundParams Array containing params for rounds:
* 0. firstRoundGuardiansNumber Number of guardians to be drafted for the first round of disputes
* 1. appealStepFactor Increasing factor for the number of guardians of each round of a dispute
* 2. maxRegularAppealRounds Number of regular appeal rounds before the final round is triggered
* 3. finalRoundLockTerms Number of terms that a coherent guardian in a final round is disallowed to withdraw (to prevent 51% attacks)
* @param _appealCollateralParams Array containing params for appeal collateral:
* 1. appealCollateralFactor Permyriad multiple of dispute fees required to appeal a preliminary ruling
* 2. appealConfirmCollateralFactor Permyriad multiple of dispute fees required to confirm appeal
* @param _minActiveBalance Minimum amount of guardian tokens that can be activated
*/
function setConfig(
uint64 _fromTermId,
IERC20 _feeToken,
uint256[3] calldata _fees,
uint64[5] calldata _roundStateDurations,
uint16[2] calldata _pcts,
uint64[4] calldata _roundParams,
uint256[2] calldata _appealCollateralParams,
uint256 _minActiveBalance
)
external
onlyConfigGovernor
{
uint64 currentTermId = _ensureCurrentTerm();
_setConfig(
currentTermId,
_fromTermId,
_feeToken,
_fees,
_roundStateDurations,
_pcts,
_roundParams,
_appealCollateralParams,
_minActiveBalance
);
}
/**
* @notice Delay the Court start time to `_newFirstTermStartTime`
* @param _newFirstTermStartTime New timestamp in seconds when the court will open
*/
function delayStartTime(uint64 _newFirstTermStartTime) external onlyConfigGovernor {
_delayStartTime(_newFirstTermStartTime);
}
/**
* @notice Change funds governor address to `_newFundsGovernor`
* @param _newFundsGovernor Address of the new funds governor to be set
*/
function changeFundsGovernor(address _newFundsGovernor) external onlyFundsGovernor {
require(_newFundsGovernor != ZERO_ADDRESS, ERROR_INVALID_GOVERNOR_ADDRESS);
_setFundsGovernor(_newFundsGovernor);
}
/**
* @notice Change config governor address to `_newConfigGovernor`
* @param _newConfigGovernor Address of the new config governor to be set
*/
function changeConfigGovernor(address _newConfigGovernor) external onlyConfigGovernor {
require(_newConfigGovernor != ZERO_ADDRESS, ERROR_INVALID_GOVERNOR_ADDRESS);
_setConfigGovernor(_newConfigGovernor);
}
/**
* @notice Change modules governor address to `_newModulesGovernor`
* @param _newModulesGovernor Address of the new governor to be set
*/
function changeModulesGovernor(address _newModulesGovernor) external onlyModulesGovernor {
require(_newModulesGovernor != ZERO_ADDRESS, ERROR_INVALID_GOVERNOR_ADDRESS);
_setModulesGovernor(_newModulesGovernor);
}
/**
* @notice Remove the funds governor. Set the funds governor to the zero address.
* @dev This action cannot be rolled back, once the funds governor has been unset, funds cannot be recovered from recoverable modules anymore
*/
function ejectFundsGovernor() external onlyFundsGovernor {
_setFundsGovernor(ZERO_ADDRESS);
}
/**
* @notice Remove the modules governor. Set the modules governor to the zero address.
* @dev This action cannot be rolled back, once the modules governor has been unset, system modules cannot be changed anymore
*/
function ejectModulesGovernor() external onlyModulesGovernor {
_setModulesGovernor(ZERO_ADDRESS);
}
/**
* @notice Grant `_id` role to `_who`
* @param _id ID of the role to be granted
* @param _who Address to grant the role to
*/
function grant(bytes32 _id, address _who) external onlyConfigGovernor {
_grant(_id, _who);
}
/**
* @notice Revoke `_id` role from `_who`
* @param _id ID of the role to be revoked
* @param _who Address to revoke the role from
*/
function revoke(bytes32 _id, address _who) external onlyConfigGovernor {
_revoke(_id, _who);
}
/**
* @notice Freeze `_id` role
* @param _id ID of the role to be frozen
*/
function freeze(bytes32 _id) external onlyConfigGovernor {
_freeze(_id);
}
/**
* @notice Enact a bulk list of ACL operations
*/
function bulk(BulkOp[] calldata _op, bytes32[] calldata _id, address[] calldata _who) external onlyConfigGovernor {
_bulk(_op, _id, _who);
}
/**
* @notice Set module `_id` to `_addr`
* @param _id ID of the module to be set
* @param _addr Address of the module to be set
*/
function setModule(bytes32 _id, address _addr) external onlyModulesGovernor {
_setModule(_id, _addr);
}
/**
* @notice Set and link many modules at once
* @param _newModuleIds List of IDs of the new modules to be set
* @param _newModuleAddresses List of addresses of the new modules to be set
* @param _newModuleLinks List of IDs of the modules that will be linked in the new modules being set
* @param _currentModulesToBeSynced List of addresses of current modules to be re-linked to the new modules being set
*/
function setModules(
bytes32[] calldata _newModuleIds,
address[] calldata _newModuleAddresses,
bytes32[] calldata _newModuleLinks,
address[] calldata _currentModulesToBeSynced
)
external
onlyModulesGovernor
{
// We only care about the modules being set, links are optional
require(_newModuleIds.length == _newModuleAddresses.length, ERROR_INVALID_IMPLS_INPUT_LENGTH);
// First set the addresses of the new modules or the modules to be updated
for (uint256 i = 0; i < _newModuleIds.length; i++) {
_setModule(_newModuleIds[i], _newModuleAddresses[i]);
}
// Then sync the links of the new modules based on the list of IDs specified (ideally the IDs of their dependencies)
_syncModuleLinks(_newModuleAddresses, _newModuleLinks);
// Finally sync the links of the existing modules to be synced to the new modules being set
_syncModuleLinks(_currentModulesToBeSynced, _newModuleIds);
}
/**
* @notice Sync modules for a list of modules IDs based on their current implementation address
* @param _modulesToBeSynced List of addresses of connected modules to be synced
* @param _idsToBeSet List of IDs of the modules included in the sync
*/
function syncModuleLinks(address[] calldata _modulesToBeSynced, bytes32[] calldata _idsToBeSet)
external
onlyModulesGovernor
{
require(_idsToBeSet.length > 0 && _modulesToBeSynced.length > 0, ERROR_INVALID_IMPLS_INPUT_LENGTH);
_syncModuleLinks(_modulesToBeSynced, _idsToBeSet);
}
/**
* @notice Disable module `_addr`
* @dev Current modules can be disabled to allow pausing the court. However, these can be enabled back again, see `enableModule`
* @param _addr Address of the module to be disabled
*/
function disableModule(address _addr) external onlyModulesGovernor {
Module storage module = allModules[_addr];
_ensureModuleExists(module);
require(!module.disabled, ERROR_MODULE_ALREADY_DISABLED);
module.disabled = true;
emit ModuleDisabled(module.id, _addr);
}
/**
* @notice Enable module `_addr`
* @param _addr Address of the module to be enabled
*/
function enableModule(address _addr) external onlyModulesGovernor {
Module storage module = allModules[_addr];
_ensureModuleExists(module);
require(module.disabled, ERROR_MODULE_ALREADY_ENABLED);
module.disabled = false;
emit ModuleEnabled(module.id, _addr);
}
/**
* @notice Set custom function `_sig` for `_target`
* @param _sig Signature of the function to be set
* @param _target Address of the target implementation to be registered for the given signature
*/
function setCustomFunction(bytes4 _sig, address _target) external onlyModulesGovernor {
customFunctions[_sig] = _target;
emit CustomFunctionSet(_sig, _target);
}
/**
* @dev Tell the full Court configuration parameters at a certain term
* @param _termId Identification number of the term querying the Court config of
* @return token Address of the token used to pay for fees
* @return fees Array containing:
* 0. guardianFee Amount of fee tokens that is paid per guardian per dispute
* 1. draftFee Amount of fee tokens per guardian to cover the drafting cost
* 2. settleFee Amount of fee tokens per guardian to cover round settlement cost
* @return roundStateDurations Array containing the durations in terms of the different phases of a dispute:
* 0. evidenceTerms Max submitting evidence period duration in terms
* 1. commitTerms Commit period duration in terms
* 2. revealTerms Reveal period duration in terms
* 3. appealTerms Appeal period duration in terms
* 4. appealConfirmationTerms Appeal confirmation period duration in terms
* @return pcts Array containing:
* 0. penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
* 1. finalRoundReduction Permyriad of fee reduction for the last appeal round (‱ - 1/10,000)
* @return roundParams Array containing params for rounds:
* 0. firstRoundGuardiansNumber Number of guardians to be drafted for the first round of disputes
* 1. appealStepFactor Increasing factor for the number of guardians of each round of a dispute
* 2. maxRegularAppealRounds Number of regular appeal rounds before the final round is triggered
* 3. finalRoundLockTerms Number of terms that a coherent guardian in a final round is disallowed to withdraw (to prevent 51% attacks)
* @return appealCollateralParams Array containing params for appeal collateral:
* 0. appealCollateralFactor Multiple of dispute fees required to appeal a preliminary ruling
* 1. appealConfirmCollateralFactor Multiple of dispute fees required to confirm appeal
*/
function getConfig(uint64 _termId) external view
returns (
IERC20 feeToken,
uint256[3] memory fees,
uint64[5] memory roundStateDurations,
uint16[2] memory pcts,
uint64[4] memory roundParams,
uint256[2] memory appealCollateralParams,
uint256 minActiveBalance
)
{
uint64 lastEnsuredTermId = _lastEnsuredTermId();
return _getConfigAt(_termId, lastEnsuredTermId);
}
/**
* @dev Tell the draft config at a certain term
* @param _termId Identification number of the term querying the draft config of
* @return feeToken Address of the token used to pay for fees
* @return draftFee Amount of fee tokens per guardian to cover the drafting cost
* @return penaltyPct Permyriad of min active tokens balance to be locked for each drafted guardian (‱ - 1/10,000)
*/
function getDraftConfig(uint64 _termId) external view returns (IERC20 feeToken, uint256 draftFee, uint16 penaltyPct) {
uint64 lastEnsuredTermId = _lastEnsuredTermId();
return _getDraftConfig(_termId, lastEnsuredTermId);
}
/**
* @dev Tell the min active balance config at a certain term
* @param _termId Identification number of the term querying the min active balance config of
* @return Minimum amount of tokens guardians have to activate to participate in the Court
*/
function getMinActiveBalance(uint64 _termId) external view returns (uint256) {
uint64 lastEnsuredTermId = _lastEnsuredTermId();
return _getMinActiveBalance(_termId, lastEnsuredTermId);
}
/**
* @dev Tell the address of the funds governor
* @return Address of the funds governor
*/
function getFundsGovernor() external view returns (address) {
return governor.funds;
}
/**
* @dev Tell the address of the config governor
* @return Address of the config governor
*/
function getConfigGovernor() external view returns (address) {
return governor.config;
}
/**
* @dev Tell the address of the modules governor
* @return Address of the modules governor
*/
function getModulesGovernor() external view returns (address) {
return governor.modules;
}
/**
* @dev Tell if a given module is active
* @param _id ID of the module to be checked
* @param _addr Address of the module to be checked
* @return True if the given module address has the requested ID and is enabled
*/
function isActive(bytes32 _id, address _addr) external view returns (bool) {
Module storage module = allModules[_addr];
return module.id == _id && !module.disabled;
}
/**
* @dev Tell the current ID and disable status of a module based on a given address
* @param _addr Address of the requested module
* @return id ID of the module being queried
* @return disabled Whether the module has been disabled
*/
function getModuleByAddress(address _addr) external view returns (bytes32 id, bool disabled) {
Module storage module = allModules[_addr];
id = module.id;
disabled = module.disabled;
}
/**
* @dev Tell the current address and disable status of a module based on a given ID
* @param _id ID of the module being queried
* @return addr Current address of the requested module
* @return disabled Whether the module has been disabled
*/
function getModule(bytes32 _id) external view returns (address addr, bool disabled) {
return _getModule(_id);
}
/**
* @dev Tell the information for the current DisputeManager module
* @return addr Current address of the DisputeManager module
* @return disabled Whether the module has been disabled
*/
function getDisputeManager() external view returns (address addr, bool disabled) {
return _getModule(MODULE_ID_DISPUTE_MANAGER);
}
/**
* @dev Tell the information for the current GuardiansRegistry module
* @return addr Current address of the GuardiansRegistry module
* @return disabled Whether the module has been disabled
*/
function getGuardiansRegistry() external view returns (address addr, bool disabled) {
return _getModule(MODULE_ID_GUARDIANS_REGISTRY);
}
/**
* @dev Tell the information for the current Voting module
* @return addr Current address of the Voting module
* @return disabled Whether the module has been disabled
*/
function getVoting() external view returns (address addr, bool disabled) {
return _getModule(MODULE_ID_VOTING);
}
/**
* @dev Tell the information for the current PaymentsBook module
* @return addr Current address of the PaymentsBook module
* @return disabled Whether the module has been disabled
*/
function getPaymentsBook() external view returns (address addr, bool disabled) {
return _getModule(MODULE_ID_PAYMENTS_BOOK);
}
/**
* @dev Tell the information for the current Treasury module
* @return addr Current address of the Treasury module
* @return disabled Whether the module has been disabled
*/
function getTreasury() external view returns (address addr, bool disabled) {
return _getModule(MODULE_ID_TREASURY);
}
/**
* @dev Tell the target registered for a custom function
* @param _sig Signature of the function being queried
* @return Address of the target where the function call will be forwarded
*/
function getCustomFunction(bytes4 _sig) external view returns (address) {
return customFunctions[_sig];
}
/**
* @dev Internal function to set the address of the funds governor
* @param _newFundsGovernor Address of the new config governor to be set
*/
function _setFundsGovernor(address _newFundsGovernor) internal {
emit FundsGovernorChanged(governor.funds, _newFundsGovernor);
governor.funds = _newFundsGovernor;
}
/**
* @dev Internal function to set the address of the config governor
* @param _newConfigGovernor Address of the new config governor to be set
*/
function _setConfigGovernor(address _newConfigGovernor) internal {
emit ConfigGovernorChanged(governor.config, _newConfigGovernor);
governor.config = _newConfigGovernor;
}
/**
* @dev Internal function to set the address of the modules governor
* @param _newModulesGovernor Address of the new modules governor to be set
*/
function _setModulesGovernor(address _newModulesGovernor) internal {
emit ModulesGovernorChanged(governor.modules, _newModulesGovernor);
governor.modules = _newModulesGovernor;
}
/**
* @dev Internal function to set an address as the current implementation for a module
* Note that the disabled condition is not affected, if the module was not set before it will be enabled by default
* @param _id Id of the module to be set
* @param _addr Address of the module to be set
*/
function _setModule(bytes32 _id, address _addr) internal {
require(isContract(_addr), ERROR_IMPLEMENTATION_NOT_CONTRACT);
currentModules[_id] = _addr;
allModules[_addr].id = _id;
emit ModuleSet(_id, _addr);
}
/**
* @dev Internal function to sync the modules for a list of modules IDs based on their current implementation address
* @param _modulesToBeSynced List of addresses of connected modules to be synced
* @param _idsToBeSet List of IDs of the modules to be linked
*/
function _syncModuleLinks(address[] memory _modulesToBeSynced, bytes32[] memory _idsToBeSet) internal {
address[] memory addressesToBeSet = new address[](_idsToBeSet.length);
// Load the addresses associated with the requested module ids
for (uint256 i = 0; i < _idsToBeSet.length; i++) {
address moduleAddress = _getModuleAddress(_idsToBeSet[i]);
Module storage module = allModules[moduleAddress];
_ensureModuleExists(module);
addressesToBeSet[i] = moduleAddress;
}
// Update the links of all the requested modules
for (uint256 j = 0; j < _modulesToBeSynced.length; j++) {
IModulesLinker(_modulesToBeSynced[j]).linkModules(_idsToBeSet, addressesToBeSet);
}
}
/**
* @dev Internal function to notify when a term has been transitioned
* @param _termId Identification number of the new current term that has been transitioned
*/
function _onTermTransitioned(uint64 _termId) internal {
_ensureTermConfig(_termId);
}
/**
* @dev Internal function to check if a module was set
* @param _module Module to be checked
*/
function _ensureModuleExists(Module storage _module) internal view {
require(_module.id != bytes32(0), ERROR_MODULE_NOT_SET);
}
/**
* @dev Internal function to tell the information for a module based on a given ID
* @param _id ID of the module being queried
* @return addr Current address of the requested module
* @return disabled Whether the module has been disabled
*/
function _getModule(bytes32 _id) internal view returns (address addr, bool disabled) {
addr = _getModuleAddress(_id);
disabled = _isModuleDisabled(addr);
}
/**
* @dev Tell the current address for a module by ID
* @param _id ID of the module being queried
* @return Current address of the requested module
*/
function _getModuleAddress(bytes32 _id) internal view returns (address) {
return currentModules[_id];
}
/**
* @dev Tell whether a module is disabled
* @param _addr Address of the module being queried
* @return True if the module is disabled, false otherwise
*/
function _isModuleDisabled(address _addr) internal view returns (bool) {
return allModules[_addr].disabled;
}
}
contract AragonCourt is IArbitrator, Controller {
using Uint256Helpers for uint256;
/**
* @dev Constructor function
* @param _termParams Array containing:
* 0. _termDuration Duration in seconds per term
* 1. _firstTermStartTime Timestamp in seconds when the court will open (to give time for guardian on-boarding)
* @param _governors Array containing:
* 0. _fundsGovernor Address of the funds governor
* 1. _configGovernor Address of the config governor
* 2. _modulesGovernor Address of the modules governor
* @param _feeToken Address of the token contract that is used to pay for fees
* @param _fees Array containing:
* 0. guardianFee Amount of fee tokens that is paid per guardian per dispute
* 1. draftFee Amount of fee tokens per guardian to cover the drafting cost
* 2. settleFee Amount of fee tokens per guardian to cover round settlement cost
* @param _roundStateDurations Array containing the durations in terms of the different phases of a dispute:
* 0. evidenceTerms Max submitting evidence period duration in terms
* 1. commitTerms Commit period duration in terms
* 2. revealTerms Reveal period duration in terms
* 3. appealTerms Appeal period duration in terms
* 4. appealConfirmationTerms Appeal confirmation period duration in terms
* @param _pcts Array containing:
* 0. penaltyPct Permyriad of min active tokens balance to be locked to each drafted guardians (‱ - 1/10,000)
* 1. finalRoundReduction Permyriad of fee reduction for the last appeal round (‱ - 1/10,000)
* @param _roundParams Array containing params for rounds:
* 0. firstRoundGuardiansNumber Number of guardians to be drafted for the first round of disputes
* 1. appealStepFactor Increasing factor for the number of guardians of each round of a dispute
* 2. maxRegularAppealRounds Number of regular appeal rounds before the final round is triggered
* 3. finalRoundLockTerms Number of terms that a coherent guardian in a final round is disallowed to withdraw (to prevent 51% attacks)
* @param _appealCollateralParams Array containing params for appeal collateral:
* 1. appealCollateralFactor Permyriad multiple of dispute fees required to appeal a preliminary ruling
* 2. appealConfirmCollateralFactor Permyriad multiple of dispute fees required to confirm appeal
* @param _minActiveBalance Minimum amount of guardian tokens that can be activated
*/
constructor(
uint64[2] memory _termParams,
address[3] memory _governors,
IERC20 _feeToken,
uint256[3] memory _fees,
uint64[5] memory _roundStateDurations,
uint16[2] memory _pcts,
uint64[4] memory _roundParams,
uint256[2] memory _appealCollateralParams,
uint256 _minActiveBalance
)
public
Controller(
_termParams,
_governors,
_feeToken,
_fees,
_roundStateDurations,
_pcts,
_roundParams,
_appealCollateralParams,
_minActiveBalance
)
{
// solium-disable-previous-line no-empty-blocks
}
/**
* @notice Create a dispute with `_possibleRulings` possible rulings
* @param _possibleRulings Number of possible rulings allowed for the drafted guardians to vote on the dispute
* @param _metadata Optional metadata that can be used to provide additional information on the dispute to be created
* @return Dispute identification number
*/
function createDispute(uint256 _possibleRulings, bytes calldata _metadata) external returns (uint256) {
IArbitrable subject = IArbitrable(msg.sender);
return _disputeManager().createDispute(subject, _possibleRulings.toUint8(), _metadata);
}
/**
* @notice Submit `_evidence` as evidence from `_submitter` for dispute #`_disputeId`
* @param _disputeId Id of the dispute in the Court
* @param _submitter Address of the account submitting the evidence
* @param _evidence Data submitted for the evidence related to the dispute
*/
function submitEvidence(uint256 _disputeId, address _submitter, bytes calldata _evidence) external {
_submitEvidence(_disputeManager(), _disputeId, _submitter, _evidence);
}
/**
* @notice Submit `_evidence` as evidence from `_submitter` for dispute #`_disputeId`
* @dev This entry point can be used to submit evidences to previous Dispute Manager instances
* @param _disputeManager Dispute manager to be used
* @param _disputeId Id of the dispute in the Court
* @param _submitter Address of the account submitting the evidence
* @param _evidence Data submitted for the evidence related to the dispute
*/
function submitEvidenceForModule(IDisputeManager _disputeManager, uint256 _disputeId, address _submitter, bytes calldata _evidence)
external
onlyActiveDisputeManager(_disputeManager)
{
_submitEvidence(_disputeManager, _disputeId, _submitter, _evidence);
}
/**
* @notice Close the evidence period of dispute #`_disputeId`
* @param _disputeId Identification number of the dispute to close its evidence submitting period
*/
function closeEvidencePeriod(uint256 _disputeId) external {
_closeEvidencePeriod(_disputeManager(), _disputeId);
}
/**
* @notice Close the evidence period of dispute #`_disputeId`
* @dev This entry point can be used to close evidence periods on previous Dispute Manager instances
* @param _disputeManager Dispute manager to be used
* @param _disputeId Identification number of the dispute to close its evidence submitting period
*/
function closeEvidencePeriodForModule(IDisputeManager _disputeManager, uint256 _disputeId)
external
onlyActiveDisputeManager(_disputeManager)
{
_closeEvidencePeriod(_disputeManager, _disputeId);
}
/**
* @notice Rule dispute #`_disputeId` if ready
* @param _disputeId Identification number of the dispute to be ruled
* @return subject Arbitrable instance associated to the dispute
* @return ruling Ruling number computed for the given dispute
*/
function rule(uint256 _disputeId) external returns (address subject, uint256 ruling) {
return _rule(_disputeManager(), _disputeId);
}
/**
* @notice Rule dispute #`_disputeId` if ready
* @dev This entry point can be used to rule disputes on previous Dispute Manager instances
* @param _disputeManager Dispute manager to be used
* @param _disputeId Identification number of the dispute to be ruled
* @return subject Arbitrable instance associated to the dispute
* @return ruling Ruling number computed for the given dispute
*/
function ruleForModule(IDisputeManager _disputeManager, uint256 _disputeId)
external
onlyActiveDisputeManager(_disputeManager)
returns (address subject, uint256 ruling)
{
return _rule(_disputeManager, _disputeId);
}
/**
* @dev Tell the dispute fees information to create a dispute
* @return recipient Address where the corresponding dispute fees must be transferred to
* @return feeToken ERC20 token used for the fees
* @return feeAmount Total amount of fees that must be allowed to the recipient
*/
function getDisputeFees() external view returns (address recipient, IERC20 feeToken, uint256 feeAmount) {
IDisputeManager disputeManager = _disputeManager();
recipient = address(disputeManager);
(feeToken, feeAmount) = disputeManager.getDisputeFees();
}
/**
* @dev Tell the payments recipient address
* @return Address of the payments recipient module
*/
function getPaymentsRecipient() external view returns (address) {
return currentModules[MODULE_ID_PAYMENTS_BOOK];
}
/**
* @dev Internal function to submit evidence for a dispute
* @param _disputeManager Dispute manager to be used
* @param _disputeId Id of the dispute in the Court
* @param _submitter Address of the account submitting the evidence
* @param _evidence Data submitted for the evidence related to the dispute
*/
function _submitEvidence(IDisputeManager _disputeManager, uint256 _disputeId, address _submitter, bytes memory _evidence) internal {
IArbitrable subject = IArbitrable(msg.sender);
_disputeManager.submitEvidence(subject, _disputeId, _submitter, _evidence);
}
/**
* @dev Internal function to close the evidence period of a dispute
* @param _disputeManager Dispute manager to be used
* @param _disputeId Identification number of the dispute to close its evidence submitting period
*/
function _closeEvidencePeriod(IDisputeManager _disputeManager, uint256 _disputeId) internal {
IArbitrable subject = IArbitrable(msg.sender);
_disputeManager.closeEvidencePeriod(subject, _disputeId);
}
/**
* @dev Internal function to rule a dispute
* @param _disputeManager Dispute manager to be used
* @param _disputeId Identification number of the dispute to be ruled
* @return subject Arbitrable instance associated to the dispute
* @return ruling Ruling number computed for the given dispute
*/
function _rule(IDisputeManager _disputeManager, uint256 _disputeId) internal returns (address subject, uint256 ruling) {
(IArbitrable _subject, uint8 _ruling) = _disputeManager.computeRuling(_disputeId);
return (address(_subject), uint256(_ruling));
}
/**
* @dev Internal function to tell the current DisputeManager module
* @return Current DisputeManager module
*/
function _disputeManager() internal view returns (IDisputeManager) {
return IDisputeManager(_getModuleAddress(MODULE_ID_DISPUTE_MANAGER));
}
}
{
"compilationTarget": {
"AragonCourt.sol": "AragonCourt"
},
"evmVersion": "istanbul",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 400
},
"remappings": []
}
[{"inputs":[{"internalType":"uint64[2]","name":"_termParams","type":"uint64[2]"},{"internalType":"address[3]","name":"_governors","type":"address[3]"},{"internalType":"contract IERC20","name":"_feeToken","type":"address"},{"internalType":"uint256[3]","name":"_fees","type":"uint256[3]"},{"internalType":"uint64[5]","name":"_roundStateDurations","type":"uint64[5]"},{"internalType":"uint16[2]","name":"_pcts","type":"uint16[2]"},{"internalType":"uint64[4]","name":"_roundParams","type":"uint64[4]"},{"internalType":"uint256[2]","name":"_appealCollateralParams","type":"uint256[2]"},{"internalType":"uint256","name":"_minActiveBalance","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousGovernor","type":"address"},{"indexed":false,"internalType":"address","name":"currentGovernor","type":"address"}],"name":"ConfigGovernorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes4","name":"signature","type":"bytes4"},{"indexed":false,"internalType":"address","name":"target","type":"address"}],"name":"CustomFunctionSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"Frozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousGovernor","type":"address"},{"indexed":false,"internalType":"address","name":"currentGovernor","type":"address"}],"name":"FundsGovernorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"address","name":"who","type":"address"}],"name":"Granted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"previousTermId","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"currentTermId","type":"uint64"}],"name":"Heartbeat","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"ModuleDisabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"ModuleEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"ModuleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousGovernor","type":"address"},{"indexed":false,"internalType":"address","name":"currentGovernor","type":"address"}],"name":"ModulesGovernorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"fromTermId","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"courtConfigId","type":"uint64"}],"name":"NewConfig","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"address","name":"who","type":"address"}],"name":"Revoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"previousStartTime","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"currentStartTime","type":"uint64"}],"name":"StartTimeDelayed","type":"event"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"constant":false,"inputs":[{"internalType":"enum ACL.BulkOp[]","name":"_op","type":"uint8[]"},{"internalType":"bytes32[]","name":"_id","type":"bytes32[]"},{"internalType":"address[]","name":"_who","type":"address[]"}],"name":"bulk","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_newConfigGovernor","type":"address"}],"name":"changeConfigGovernor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_newFundsGovernor","type":"address"}],"name":"changeFundsGovernor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_newModulesGovernor","type":"address"}],"name":"changeModulesGovernor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_disputeId","type":"uint256"}],"name":"closeEvidencePeriod","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IDisputeManager","name":"_disputeManager","type":"address"},{"internalType":"uint256","name":"_disputeId","type":"uint256"}],"name":"closeEvidencePeriodForModule","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_possibleRulings","type":"uint256"},{"internalType":"bytes","name":"_metadata","type":"bytes"}],"name":"createDispute","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint64","name":"_newFirstTermStartTime","type":"uint64"}],"name":"delayStartTime","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"disableModule","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"ejectFundsGovernor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"ejectModulesGovernor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"enableModule","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"ensureCurrentTerm","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"ensureCurrentTermRandomness","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"}],"name":"freeze","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint64","name":"_termId","type":"uint64"}],"name":"getConfig","outputs":[{"internalType":"contract IERC20","name":"feeToken","type":"address"},{"internalType":"uint256[3]","name":"fees","type":"uint256[3]"},{"internalType":"uint64[5]","name":"roundStateDurations","type":"uint64[5]"},{"internalType":"uint16[2]","name":"pcts","type":"uint16[2]"},{"internalType":"uint64[4]","name":"roundParams","type":"uint64[4]"},{"internalType":"uint256[2]","name":"appealCollateralParams","type":"uint256[2]"},{"internalType":"uint256","name":"minActiveBalance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getConfigChangeTermId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getConfigGovernor","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentTermId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes4","name":"_sig","type":"bytes4"}],"name":"getCustomFunction","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getDisputeFees","outputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"contract IERC20","name":"feeToken","type":"address"},{"internalType":"uint256","name":"feeAmount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getDisputeManager","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"disabled","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint64","name":"_termId","type":"uint64"}],"name":"getDraftConfig","outputs":[{"internalType":"contract IERC20","name":"feeToken","type":"address"},{"internalType":"uint256","name":"draftFee","type":"uint256"},{"internalType":"uint16","name":"penaltyPct","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getFundsGovernor","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getGuardiansRegistry","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"disabled","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getLastEnsuredTermId","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint64","name":"_termId","type":"uint64"}],"name":"getMinActiveBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"}],"name":"getModule","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"disabled","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"getModuleByAddress","outputs":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"bool","name":"disabled","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getModulesGovernor","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getNeededTermTransitions","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPaymentsBook","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"disabled","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPaymentsRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint64","name":"_termId","type":"uint64"}],"name":"getTerm","outputs":[{"internalType":"uint64","name":"startTime","type":"uint64"},{"internalType":"uint64","name":"randomnessBN","type":"uint64"},{"internalType":"bytes32","name":"randomness","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTermDuration","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint64","name":"_termId","type":"uint64"}],"name":"getTermRandomness","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTreasury","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"disabled","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getVoting","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"disabled","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"},{"internalType":"address","name":"_who","type":"address"}],"name":"grant","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_who","type":"address"},{"internalType":"bytes32","name":"_id","type":"bytes32"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint64","name":"_maxRequestedTransitions","type":"uint64"}],"name":"heartbeat","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"},{"internalType":"address","name":"_addr","type":"address"}],"name":"isActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"}],"name":"isRoleFrozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"},{"internalType":"address","name":"_who","type":"address"}],"name":"revoke","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"}],"name":"roles","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_disputeId","type":"uint256"}],"name":"rule","outputs":[{"internalType":"address","name":"subject","type":"address"},{"internalType":"uint256","name":"ruling","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IDisputeManager","name":"_disputeManager","type":"address"},{"internalType":"uint256","name":"_disputeId","type":"uint256"}],"name":"ruleForModule","outputs":[{"internalType":"address","name":"subject","type":"address"},{"internalType":"uint256","name":"ruling","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint64","name":"_fromTermId","type":"uint64"},{"internalType":"contract IERC20","name":"_feeToken","type":"address"},{"internalType":"uint256[3]","name":"_fees","type":"uint256[3]"},{"internalType":"uint64[5]","name":"_roundStateDurations","type":"uint64[5]"},{"internalType":"uint16[2]","name":"_pcts","type":"uint16[2]"},{"internalType":"uint64[4]","name":"_roundParams","type":"uint64[4]"},{"internalType":"uint256[2]","name":"_appealCollateralParams","type":"uint256[2]"},{"internalType":"uint256","name":"_minActiveBalance","type":"uint256"}],"name":"setConfig","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes4","name":"_sig","type":"bytes4"},{"internalType":"address","name":"_target","type":"address"}],"name":"setCustomFunction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"_id","type":"bytes32"},{"internalType":"address","name":"_addr","type":"address"}],"name":"setModule","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32[]","name":"_newModuleIds","type":"bytes32[]"},{"internalType":"address[]","name":"_newModuleAddresses","type":"address[]"},{"internalType":"bytes32[]","name":"_newModuleLinks","type":"bytes32[]"},{"internalType":"address[]","name":"_currentModulesToBeSynced","type":"address[]"}],"name":"setModules","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_disputeId","type":"uint256"},{"internalType":"address","name":"_submitter","type":"address"},{"internalType":"bytes","name":"_evidence","type":"bytes"}],"name":"submitEvidence","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract IDisputeManager","name":"_disputeManager","type":"address"},{"internalType":"uint256","name":"_disputeId","type":"uint256"},{"internalType":"address","name":"_submitter","type":"address"},{"internalType":"bytes","name":"_evidence","type":"bytes"}],"name":"submitEvidenceForModule","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address[]","name":"_modulesToBeSynced","type":"address[]"},{"internalType":"bytes32[]","name":"_idsToBeSet","type":"bytes32[]"}],"name":"syncModuleLinks","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]