Overview
ETH Balance
3.606744143 ETH
Eth Value
$9,447.11 (@ $2,619.29/ETH)Token Holdings
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 364 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw | 7191099 | 2098 days ago | IN | 0 ETH | 0.00163599 | ||||
Withdraw | 7190259 | 2098 days ago | IN | 0 ETH | 0.00018107 | ||||
Withdraw | 7187513 | 2099 days ago | IN | 0 ETH | 0.00020437 | ||||
Withdraw | 7176752 | 2101 days ago | IN | 0 ETH | 0.00048282 | ||||
Withdraw | 7156138 | 2105 days ago | IN | 0 ETH | 0.00030837 | ||||
Withdraw | 7156132 | 2105 days ago | IN | 0 ETH | 0.00048112 | ||||
Withdraw | 7146895 | 2107 days ago | IN | 0 ETH | 0.00038761 | ||||
Withdraw | 7146892 | 2107 days ago | IN | 0 ETH | 0.0002725 | ||||
Withdraw | 7146889 | 2107 days ago | IN | 0 ETH | 0.00041498 | ||||
Withdraw | 7146885 | 2107 days ago | IN | 0 ETH | 0.00034363 | ||||
Withdraw | 7146519 | 2107 days ago | IN | 0 ETH | 0.00030995 | ||||
Withdraw | 7146516 | 2107 days ago | IN | 0 ETH | 0.00030993 | ||||
Withdraw | 7146512 | 2107 days ago | IN | 0 ETH | 0.00020743 | ||||
Withdraw | 7146499 | 2107 days ago | IN | 0 ETH | 0.00030382 | ||||
Withdraw | 7146497 | 2107 days ago | IN | 0 ETH | 0.00016319 | ||||
Withdraw | 7141972 | 2108 days ago | IN | 0 ETH | 0.00060353 | ||||
Withdraw | 7138881 | 2109 days ago | IN | 0 ETH | 0.00039579 | ||||
Withdraw | 7138880 | 2109 days ago | IN | 0 ETH | 0.00069036 | ||||
Withdraw | 7138878 | 2109 days ago | IN | 0 ETH | 0.00039643 | ||||
Withdraw | 7138876 | 2109 days ago | IN | 0 ETH | 0.00069351 | ||||
Withdraw | 7138874 | 2109 days ago | IN | 0 ETH | 0.00045333 | ||||
Withdraw | 7121275 | 2112 days ago | IN | 0 ETH | 0.0002705 | ||||
Withdraw | 7121271 | 2112 days ago | IN | 0 ETH | 0.00034299 | ||||
Withdraw | 7121269 | 2112 days ago | IN | 0 ETH | 0.00042143 | ||||
Withdraw | 7121263 | 2112 days ago | IN | 0 ETH | 0.00022708 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
7190259 | 2098 days ago | 0.076 ETH | ||||
7187513 | 2099 days ago | 1.09281 ETH | ||||
7176752 | 2101 days ago | 1.5 ETH | ||||
7156132 | 2105 days ago | 0.024 ETH | ||||
7146892 | 2107 days ago | 8.9022556 ETH | ||||
7146497 | 2107 days ago | 7.5660472 ETH | ||||
7141972 | 2108 days ago | 4.1 ETH | ||||
7138874 | 2109 days ago | 4.2662174 ETH | ||||
7121263 | 2112 days ago | 10.5614279 ETH | ||||
7118457 | 2113 days ago | 4.4508637 ETH | ||||
7115980 | 2113 days ago | 0.00576 ETH | ||||
7113319 | 2114 days ago | 0.0036936 ETH | ||||
7113273 | 2114 days ago | 0.35904131 ETH | ||||
7113223 | 2114 days ago | 0.0023996 ETH | ||||
7112496 | 2114 days ago | 0.00387 ETH | ||||
7109796 | 2114 days ago | 0.005136 ETH | ||||
7108602 | 2115 days ago | 0.0030752 ETH | ||||
7108581 | 2115 days ago | 0.005706 ETH | ||||
7099276 | 2116 days ago | 0.004515 ETH | ||||
7099247 | 2116 days ago | 0.004264 ETH | ||||
7098836 | 2116 days ago | 1.01 ETH | ||||
7096156 | 2117 days ago | 0.0049 ETH | ||||
7085224 | 2119 days ago | 0.00632 ETH | ||||
7084312 | 2119 days ago | 0.004438 ETH | ||||
7084210 | 2119 days ago | 0.0043758 ETH |
Loading...
Loading
Contract Name:
RenExBalances
Compiler Version
v0.4.24+commit.e67f0147
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2018-09-27 */ pragma solidity 0.4.24; /** * @title ERC20Basic * @dev Simpler version of ERC20 interface * See https://github.com/ethereum/EIPs/issues/179 */ contract ERC20Basic { function totalSupply() public view returns (uint256); function balanceOf(address who) public view returns (uint256); function transfer(address to, uint256 value) public returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); } /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ contract ERC20 is ERC20Basic { function allowance(address owner, address spender) public view returns (uint256); function transferFrom(address from, address to, uint256 value) public returns (bool); function approve(address spender, uint256 value) public returns (bool); event Approval( address indexed owner, address indexed spender, uint256 value ); } /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Multiplies two numbers, throws on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) { // Gas optimization: this is cheaper than asserting 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } c = a * b; assert(c / a == b); return c; } /** * @dev Integer division of two numbers, truncating the quotient. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 // uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return a / b; } /** * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } /** * @dev Adds two numbers, throws on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256 c) { c = a + b; assert(c >= a); return c; } } /** * @title Ownable * @dev The Ownable contract has an owner address, and provides basic authorization control * functions, this simplifies the implementation of "user permissions". */ contract Ownable { address public owner; event OwnershipRenounced(address indexed previousOwner); event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor() public { owner = msg.sender; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner); _; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipRenounced(owner); owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function transferOwnership(address _newOwner) public onlyOwner { _transferOwnership(_newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param _newOwner The address to transfer ownership to. */ function _transferOwnership(address _newOwner) internal { require(_newOwner != address(0)); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } } /** * @title Basic token * @dev Basic version of StandardToken, with no allowances. */ contract BasicToken is ERC20Basic { using SafeMath for uint256; mapping(address => uint256) balances; uint256 totalSupply_; /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return totalSupply_; } /** * @dev Transfer token for a specified address * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint256 _value) public returns (bool) { require(_to != address(0)); require(_value <= balances[msg.sender]); balances[msg.sender] = balances[msg.sender].sub(_value); balances[_to] = balances[_to].add(_value); emit Transfer(msg.sender, _to, _value); return true; } /** * @dev Gets the balance of the specified address. * @param _owner The address to query the the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address _owner) public view returns (uint256) { return balances[_owner]; } } /** * @title Standard ERC20 token * * @dev Implementation of the basic standard token. * https://github.com/ethereum/EIPs/issues/20 * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol */ contract StandardToken is ERC20, BasicToken { mapping (address => mapping (address => uint256)) internal allowed; /** * @dev Transfer tokens from one address to another * @param _from address The address which you want to send tokens from * @param _to address The address which you want to transfer to * @param _value uint256 the amount of tokens to be transferred */ function transferFrom( address _from, address _to, uint256 _value ) public returns (bool) { require(_to != address(0)); require(_value <= balances[_from]); require(_value <= allowed[_from][msg.sender]); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(_value); allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); emit Transfer(_from, _to, _value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param _spender The address which will spend the funds. * @param _value The amount of tokens to be spent. */ function approve(address _spender, uint256 _value) public returns (bool) { allowed[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param _owner address The address which owns the funds. * @param _spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance( address _owner, address _spender ) public view returns (uint256) { return allowed[_owner][_spender]; } /** * @dev Increase the amount of tokens that an owner allowed to a spender. * approve should be called when allowed[_spender] == 0. To increment * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * @param _spender The address which will spend the funds. * @param _addedValue The amount of tokens to increase the allowance by. */ function increaseApproval( address _spender, uint256 _addedValue ) public returns (bool) { allowed[msg.sender][_spender] = ( allowed[msg.sender][_spender].add(_addedValue)); emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; } /** * @dev Decrease the amount of tokens that an owner allowed to a spender. * approve should be called when allowed[_spender] == 0. To decrement * allowed value is better to use this function to avoid 2 calls (and wait until * the first transaction is mined) * From MonolithDAO Token.sol * @param _spender The address which will spend the funds. * @param _subtractedValue The amount of tokens to decrease the allowance by. */ function decreaseApproval( address _spender, uint256 _subtractedValue ) public returns (bool) { uint256 oldValue = allowed[msg.sender][_spender]; if (_subtractedValue > oldValue) { allowed[msg.sender][_spender] = 0; } else { allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue); } emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]); return true; } } /** * @title Pausable * @dev Base contract which allows children to implement an emergency stop mechanism. */ contract Pausable is Ownable { event Pause(); event Unpause(); bool public paused = false; /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!paused); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() onlyOwner whenNotPaused public { paused = true; emit Pause(); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() onlyOwner whenPaused public { paused = false; emit Unpause(); } } /** * @title Pausable token * @dev StandardToken modified with pausable transfers. **/ contract PausableToken is StandardToken, Pausable { function transfer( address _to, uint256 _value ) public whenNotPaused returns (bool) { return super.transfer(_to, _value); } function transferFrom( address _from, address _to, uint256 _value ) public whenNotPaused returns (bool) { return super.transferFrom(_from, _to, _value); } function approve( address _spender, uint256 _value ) public whenNotPaused returns (bool) { return super.approve(_spender, _value); } function increaseApproval( address _spender, uint _addedValue ) public whenNotPaused returns (bool success) { return super.increaseApproval(_spender, _addedValue); } function decreaseApproval( address _spender, uint _subtractedValue ) public whenNotPaused returns (bool success) { return super.decreaseApproval(_spender, _subtractedValue); } } /** * @title Burnable Token * @dev Token that can be irreversibly burned (destroyed). */ contract BurnableToken is BasicToken { event Burn(address indexed burner, uint256 value); /** * @dev Burns a specific amount of tokens. * @param _value The amount of token to be burned. */ function burn(uint256 _value) public { _burn(msg.sender, _value); } function _burn(address _who, uint256 _value) internal { require(_value <= balances[_who]); // no need to require value <= totalSupply, since that would imply the // sender's balance is greater than the totalSupply, which *should* be an assertion failure balances[_who] = balances[_who].sub(_value); totalSupply_ = totalSupply_.sub(_value); emit Burn(_who, _value); emit Transfer(_who, address(0), _value); } } contract RepublicToken is PausableToken, BurnableToken { string public constant name = "Republic Token"; string public constant symbol = "REN"; uint8 public constant decimals = 18; uint256 public constant INITIAL_SUPPLY = 1000000000 * 10**uint256(decimals); /// @notice The RepublicToken Constructor. constructor() public { totalSupply_ = INITIAL_SUPPLY; balances[msg.sender] = INITIAL_SUPPLY; } function transferTokens(address beneficiary, uint256 amount) public onlyOwner returns (bool) { /* solium-disable error-reason */ require(amount > 0); balances[owner] = balances[owner].sub(amount); balances[beneficiary] = balances[beneficiary].add(amount); emit Transfer(owner, beneficiary, amount); return true; } } /** * @notice LinkedList is a library for a circular double linked list. */ library LinkedList { /* * @notice A permanent NULL node (0x0) in the circular double linked list. * NULL.next is the head, and NULL.previous is the tail. */ address public constant NULL = 0x0; /** * @notice A node points to the node before it, and the node after it. If * node.previous = NULL, then the node is the head of the list. If * node.next = NULL, then the node is the tail of the list. */ struct Node { bool inList; address previous; address next; } /** * @notice LinkedList uses a mapping from address to nodes. Each address * uniquely identifies a node, and in this way they are used like pointers. */ struct List { mapping (address => Node) list; } /** * @notice Insert a new node before an existing node. * * @param self The list being used. * @param target The existing node in the list. * @param newNode The next node to insert before the target. */ function insertBefore(List storage self, address target, address newNode) internal { require(!isInList(self, newNode), "already in list"); require(isInList(self, target) || target == NULL, "not in list"); // It is expected that this value is sometimes NULL. address prev = self.list[target].previous; self.list[newNode].next = target; self.list[newNode].previous = prev; self.list[target].previous = newNode; self.list[prev].next = newNode; self.list[newNode].inList = true; } /** * @notice Insert a new node after an existing node. * * @param self The list being used. * @param target The existing node in the list. * @param newNode The next node to insert after the target. */ function insertAfter(List storage self, address target, address newNode) internal { require(!isInList(self, newNode), "already in list"); require(isInList(self, target) || target == NULL, "not in list"); // It is expected that this value is sometimes NULL. address n = self.list[target].next; self.list[newNode].previous = target; self.list[newNode].next = n; self.list[target].next = newNode; self.list[n].previous = newNode; self.list[newNode].inList = true; } /** * @notice Remove a node from the list, and fix the previous and next * pointers that are pointing to the removed node. Removing anode that is not * in the list will do nothing. * * @param self The list being using. * @param node The node in the list to be removed. */ function remove(List storage self, address node) internal { require(isInList(self, node), "not in list"); if (node == NULL) { return; } address p = self.list[node].previous; address n = self.list[node].next; self.list[p].next = n; self.list[n].previous = p; // Deleting the node should set this value to false, but we set it here for // explicitness. self.list[node].inList = false; delete self.list[node]; } /** * @notice Insert a node at the beginning of the list. * * @param self The list being used. * @param node The node to insert at the beginning of the list. */ function prepend(List storage self, address node) internal { // isInList(node) is checked in insertBefore insertBefore(self, begin(self), node); } /** * @notice Insert a node at the end of the list. * * @param self The list being used. * @param node The node to insert at the end of the list. */ function append(List storage self, address node) internal { // isInList(node) is checked in insertBefore insertAfter(self, end(self), node); } function swap(List storage self, address left, address right) internal { // isInList(left) and isInList(right) are checked in remove address previousRight = self.list[right].previous; remove(self, right); insertAfter(self, left, right); remove(self, left); insertAfter(self, previousRight, left); } function isInList(List storage self, address node) internal view returns (bool) { return self.list[node].inList; } /** * @notice Get the node at the beginning of a double linked list. * * @param self The list being used. * * @return A address identifying the node at the beginning of the double * linked list. */ function begin(List storage self) internal view returns (address) { return self.list[NULL].next; } /** * @notice Get the node at the end of a double linked list. * * @param self The list being used. * * @return A address identifying the node at the end of the double linked * list. */ function end(List storage self) internal view returns (address) { return self.list[NULL].previous; } function next(List storage self, address node) internal view returns (address) { require(isInList(self, node), "not in list"); return self.list[node].next; } function previous(List storage self, address node) internal view returns (address) { require(isInList(self, node), "not in list"); return self.list[node].previous; } } /// @notice This contract stores data and funds for the DarknodeRegistry /// contract. The data / fund logic and storage have been separated to improve /// upgradability. contract DarknodeRegistryStore is Ownable { string public VERSION; // Passed in as a constructor parameter. /// @notice Darknodes are stored in the darknode struct. The owner is the /// address that registered the darknode, the bond is the amount of REN that /// was transferred during registration, and the public key is the /// encryption key that should be used when sending sensitive information to /// the darknode. struct Darknode { // The owner of a Darknode is the address that called the register // function. The owner is the only address that is allowed to // deregister the Darknode, unless the Darknode is slashed for // malicious behavior. address owner; // The bond is the amount of REN submitted as a bond by the Darknode. // This amount is reduced when the Darknode is slashed for malicious // behavior. uint256 bond; // The block number at which the Darknode is considered registered. uint256 registeredAt; // The block number at which the Darknode is considered deregistered. uint256 deregisteredAt; // The public key used by this Darknode for encrypting sensitive data // off chain. It is assumed that the Darknode has access to the // respective private key, and that there is an agreement on the format // of the public key. bytes publicKey; } /// Registry data. mapping(address => Darknode) private darknodeRegistry; LinkedList.List private darknodes; // RepublicToken. RepublicToken public ren; /// @notice The contract constructor. /// /// @param _VERSION A string defining the contract version. /// @param _ren The address of the RepublicToken contract. constructor( string _VERSION, RepublicToken _ren ) public { VERSION = _VERSION; ren = _ren; } /// @notice Instantiates a darknode and appends it to the darknodes /// linked-list. /// /// @param _darknodeID The darknode's ID. /// @param _darknodeOwner The darknode's owner's address /// @param _bond The darknode's bond value /// @param _publicKey The darknode's public key /// @param _registeredAt The time stamp when the darknode is registered. /// @param _deregisteredAt The time stamp when the darknode is deregistered. function appendDarknode( address _darknodeID, address _darknodeOwner, uint256 _bond, bytes _publicKey, uint256 _registeredAt, uint256 _deregisteredAt ) external onlyOwner { Darknode memory darknode = Darknode({ owner: _darknodeOwner, bond: _bond, publicKey: _publicKey, registeredAt: _registeredAt, deregisteredAt: _deregisteredAt }); darknodeRegistry[_darknodeID] = darknode; LinkedList.append(darknodes, _darknodeID); } /// @notice Returns the address of the first darknode in the store function begin() external view onlyOwner returns(address) { return LinkedList.begin(darknodes); } /// @notice Returns the address of the next darknode in the store after the /// given address. function next(address darknodeID) external view onlyOwner returns(address) { return LinkedList.next(darknodes, darknodeID); } /// @notice Removes a darknode from the store and transfers its bond to the /// owner of this contract. function removeDarknode(address darknodeID) external onlyOwner { uint256 bond = darknodeRegistry[darknodeID].bond; delete darknodeRegistry[darknodeID]; LinkedList.remove(darknodes, darknodeID); require(ren.transfer(owner, bond), "bond transfer failed"); } /// @notice Updates the bond of the darknode. If the bond is being /// decreased, the difference is sent to the owner of this contract. function updateDarknodeBond(address darknodeID, uint256 bond) external onlyOwner { uint256 previousBond = darknodeRegistry[darknodeID].bond; darknodeRegistry[darknodeID].bond = bond; if (previousBond > bond) { require(ren.transfer(owner, previousBond - bond), "cannot transfer bond"); } } /// @notice Updates the deregistration timestamp of a darknode. function updateDarknodeDeregisteredAt(address darknodeID, uint256 deregisteredAt) external onlyOwner { darknodeRegistry[darknodeID].deregisteredAt = deregisteredAt; } /// @notice Returns the owner of a given darknode. function darknodeOwner(address darknodeID) external view onlyOwner returns (address) { return darknodeRegistry[darknodeID].owner; } /// @notice Returns the bond of a given darknode. function darknodeBond(address darknodeID) external view onlyOwner returns (uint256) { return darknodeRegistry[darknodeID].bond; } /// @notice Returns the registration time of a given darknode. function darknodeRegisteredAt(address darknodeID) external view onlyOwner returns (uint256) { return darknodeRegistry[darknodeID].registeredAt; } /// @notice Returns the deregistration time of a given darknode. function darknodeDeregisteredAt(address darknodeID) external view onlyOwner returns (uint256) { return darknodeRegistry[darknodeID].deregisteredAt; } /// @notice Returns the encryption public key of a given darknode. function darknodePublicKey(address darknodeID) external view onlyOwner returns (bytes) { return darknodeRegistry[darknodeID].publicKey; } } /// @notice DarknodeRegistry is responsible for the registration and /// deregistration of Darknodes. contract DarknodeRegistry is Ownable { string public VERSION; // Passed in as a constructor parameter. /// @notice Darknode pods are shuffled after a fixed number of blocks. /// An Epoch stores an epoch hash used as an (insecure) RNG seed, and the /// blocknumber which restricts when the next epoch can be called. struct Epoch { uint256 epochhash; uint256 blocknumber; } uint256 public numDarknodes; uint256 public numDarknodesNextEpoch; uint256 public numDarknodesPreviousEpoch; /// Variables used to parameterize behavior. uint256 public minimumBond; uint256 public minimumPodSize; uint256 public minimumEpochInterval; address public slasher; /// When one of the above variables is modified, it is only updated when the /// next epoch is called. These variables store the values for the next epoch. uint256 public nextMinimumBond; uint256 public nextMinimumPodSize; uint256 public nextMinimumEpochInterval; address public nextSlasher; /// The current and previous epoch Epoch public currentEpoch; Epoch public previousEpoch; /// Republic ERC20 token contract used to transfer bonds. RepublicToken public ren; /// Darknode Registry Store is the storage contract for darknodes. DarknodeRegistryStore public store; /// @notice Emitted when a darknode is registered. /// @param _darknodeID The darknode ID that was registered. /// @param _bond The amount of REN that was transferred as bond. event LogDarknodeRegistered(address _darknodeID, uint256 _bond); /// @notice Emitted when a darknode is deregistered. /// @param _darknodeID The darknode ID that was deregistered. event LogDarknodeDeregistered(address _darknodeID); /// @notice Emitted when a refund has been made. /// @param _owner The address that was refunded. /// @param _amount The amount of REN that was refunded. event LogDarknodeOwnerRefunded(address _owner, uint256 _amount); /// @notice Emitted when a new epoch has begun. event LogNewEpoch(); /// @notice Emitted when a constructor parameter has been updated. event LogMinimumBondUpdated(uint256 previousMinimumBond, uint256 nextMinimumBond); event LogMinimumPodSizeUpdated(uint256 previousMinimumPodSize, uint256 nextMinimumPodSize); event LogMinimumEpochIntervalUpdated(uint256 previousMinimumEpochInterval, uint256 nextMinimumEpochInterval); event LogSlasherUpdated(address previousSlasher, address nextSlasher); /// @notice Only allow the owner that registered the darknode to pass. modifier onlyDarknodeOwner(address _darknodeID) { require(store.darknodeOwner(_darknodeID) == msg.sender, "must be darknode owner"); _; } /// @notice Only allow unregistered darknodes. modifier onlyRefunded(address _darknodeID) { require(isRefunded(_darknodeID), "must be refunded or never registered"); _; } /// @notice Only allow refundable darknodes. modifier onlyRefundable(address _darknodeID) { require(isRefundable(_darknodeID), "must be deregistered for at least one epoch"); _; } /// @notice Only allowed registered nodes without a pending deregistration to /// deregister modifier onlyDeregisterable(address _darknodeID) { require(isDeregisterable(_darknodeID), "must be deregisterable"); _; } /// @notice Only allow the Slasher contract. modifier onlySlasher() { require(slasher == msg.sender, "must be slasher"); _; } /// @notice The contract constructor. /// /// @param _VERSION A string defining the contract version. /// @param _renAddress The address of the RepublicToken contract. /// @param _storeAddress The address of the DarknodeRegistryStore contract. /// @param _minimumBond The minimum bond amount that can be submitted by a /// Darknode. /// @param _minimumPodSize The minimum size of a Darknode pod. /// @param _minimumEpochInterval The minimum number of blocks between /// epochs. constructor( string _VERSION, RepublicToken _renAddress, DarknodeRegistryStore _storeAddress, uint256 _minimumBond, uint256 _minimumPodSize, uint256 _minimumEpochInterval ) public { VERSION = _VERSION; store = _storeAddress; ren = _renAddress; minimumBond = _minimumBond; nextMinimumBond = minimumBond; minimumPodSize = _minimumPodSize; nextMinimumPodSize = minimumPodSize; minimumEpochInterval = _minimumEpochInterval; nextMinimumEpochInterval = minimumEpochInterval; currentEpoch = Epoch({ epochhash: uint256(blockhash(block.number - 1)), blocknumber: block.number }); numDarknodes = 0; numDarknodesNextEpoch = 0; numDarknodesPreviousEpoch = 0; } /// @notice Register a darknode and transfer the bond to this contract. The /// caller must provide a public encryption key for the darknode as well as /// a bond in REN. The bond must be provided as an ERC20 allowance. The dark /// node will remain pending registration until the next epoch. Only after /// this period can the darknode be deregistered. The caller of this method /// will be stored as the owner of the darknode. /// /// @param _darknodeID The darknode ID that will be registered. /// @param _publicKey The public key of the darknode. It is stored to allow /// other darknodes and traders to encrypt messages to the trader. /// @param _bond The bond that will be paid. It must be greater than, or /// equal to, the minimum bond. function register(address _darknodeID, bytes _publicKey, uint256 _bond) external onlyRefunded(_darknodeID) { // REN allowance require(_bond >= minimumBond, "insufficient bond"); // require(ren.allowance(msg.sender, address(this)) >= _bond); require(ren.transferFrom(msg.sender, address(this), _bond), "bond transfer failed"); ren.transfer(address(store), _bond); // Flag this darknode for registration store.appendDarknode( _darknodeID, msg.sender, _bond, _publicKey, currentEpoch.blocknumber + minimumEpochInterval, 0 ); numDarknodesNextEpoch += 1; // Emit an event. emit LogDarknodeRegistered(_darknodeID, _bond); } /// @notice Deregister a darknode. The darknode will not be deregistered /// until the end of the epoch. After another epoch, the bond can be /// refunded by calling the refund method. /// @param _darknodeID The darknode ID that will be deregistered. The caller /// of this method store.darknodeRegisteredAt(_darknodeID) must be // the owner of this darknode. function deregister(address _darknodeID) external onlyDeregisterable(_darknodeID) onlyDarknodeOwner(_darknodeID) { // Flag the darknode for deregistration store.updateDarknodeDeregisteredAt(_darknodeID, currentEpoch.blocknumber + minimumEpochInterval); numDarknodesNextEpoch -= 1; // Emit an event emit LogDarknodeDeregistered(_darknodeID); } /// @notice Progress the epoch if it is possible to do so. This captures /// the current timestamp and current blockhash and overrides the current /// epoch. function epoch() external { if (previousEpoch.blocknumber == 0) { // The first epoch must be called by the owner of the contract require(msg.sender == owner, "not authorized (first epochs)"); } // Require that the epoch interval has passed require(block.number >= currentEpoch.blocknumber + minimumEpochInterval, "epoch interval has not passed"); uint256 epochhash = uint256(blockhash(block.number - 1)); // Update the epoch hash and timestamp previousEpoch = currentEpoch; currentEpoch = Epoch({ epochhash: epochhash, blocknumber: block.number }); // Update the registry information numDarknodesPreviousEpoch = numDarknodes; numDarknodes = numDarknodesNextEpoch; // If any update functions have been called, update the values now if (nextMinimumBond != minimumBond) { minimumBond = nextMinimumBond; emit LogMinimumBondUpdated(minimumBond, nextMinimumBond); } if (nextMinimumPodSize != minimumPodSize) { minimumPodSize = nextMinimumPodSize; emit LogMinimumPodSizeUpdated(minimumPodSize, nextMinimumPodSize); } if (nextMinimumEpochInterval != minimumEpochInterval) { minimumEpochInterval = nextMinimumEpochInterval; emit LogMinimumEpochIntervalUpdated(minimumEpochInterval, nextMinimumEpochInterval); } if (nextSlasher != slasher) { slasher = nextSlasher; emit LogSlasherUpdated(slasher, nextSlasher); } // Emit an event emit LogNewEpoch(); } /// @notice Allows the contract owner to transfer ownership of the /// DarknodeRegistryStore. /// @param _newOwner The address to transfer the ownership to. function transferStoreOwnership(address _newOwner) external onlyOwner { store.transferOwnership(_newOwner); } /// @notice Allows the contract owner to update the minimum bond. /// @param _nextMinimumBond The minimum bond amount that can be submitted by /// a darknode. function updateMinimumBond(uint256 _nextMinimumBond) external onlyOwner { // Will be updated next epoch nextMinimumBond = _nextMinimumBond; } /// @notice Allows the contract owner to update the minimum pod size. /// @param _nextMinimumPodSize The minimum size of a pod. function updateMinimumPodSize(uint256 _nextMinimumPodSize) external onlyOwner { // Will be updated next epoch nextMinimumPodSize = _nextMinimumPodSize; } /// @notice Allows the contract owner to update the minimum epoch interval. /// @param _nextMinimumEpochInterval The minimum number of blocks between epochs. function updateMinimumEpochInterval(uint256 _nextMinimumEpochInterval) external onlyOwner { // Will be updated next epoch nextMinimumEpochInterval = _nextMinimumEpochInterval; } /// @notice Allow the contract owner to update the DarknodeSlasher contract /// address. /// @param _slasher The new slasher address. function updateSlasher(address _slasher) external onlyOwner { nextSlasher = _slasher; } /// @notice Allow the DarknodeSlasher contract to slash half of a darknode's /// bond and deregister it. The bond is distributed as follows: /// 1/2 is kept by the guilty prover /// 1/8 is rewarded to the first challenger /// 1/8 is rewarded to the second challenger /// 1/4 becomes unassigned /// @param _prover The guilty prover whose bond is being slashed /// @param _challenger1 The first of the two darknodes who submitted the challenge /// @param _challenger2 The second of the two darknodes who submitted the challenge function slash(address _prover, address _challenger1, address _challenger2) external onlySlasher { uint256 penalty = store.darknodeBond(_prover) / 2; uint256 reward = penalty / 4; // Slash the bond of the failed prover in half store.updateDarknodeBond(_prover, penalty); // If the darknode has not been deregistered then deregister it if (isDeregisterable(_prover)) { store.updateDarknodeDeregisteredAt(_prover, currentEpoch.blocknumber + minimumEpochInterval); numDarknodesNextEpoch -= 1; emit LogDarknodeDeregistered(_prover); } // Reward the challengers with less than the penalty so that it is not // worth challenging yourself ren.transfer(store.darknodeOwner(_challenger1), reward); ren.transfer(store.darknodeOwner(_challenger2), reward); } /// @notice Refund the bond of a deregistered darknode. This will make the /// darknode available for registration again. Anyone can call this function /// but the bond will always be refunded to the darknode owner. /// /// @param _darknodeID The darknode ID that will be refunded. The caller /// of this method must be the owner of this darknode. function refund(address _darknodeID) external onlyRefundable(_darknodeID) { address darknodeOwner = store.darknodeOwner(_darknodeID); // Remember the bond amount uint256 amount = store.darknodeBond(_darknodeID); // Erase the darknode from the registry store.removeDarknode(_darknodeID); // Refund the owner by transferring REN ren.transfer(darknodeOwner, amount); // Emit an event. emit LogDarknodeOwnerRefunded(darknodeOwner, amount); } /// @notice Retrieves the address of the account that registered a darknode. /// @param _darknodeID The ID of the darknode to retrieve the owner for. function getDarknodeOwner(address _darknodeID) external view returns (address) { return store.darknodeOwner(_darknodeID); } /// @notice Retrieves the bond amount of a darknode in 10^-18 REN. /// @param _darknodeID The ID of the darknode to retrieve the bond for. function getDarknodeBond(address _darknodeID) external view returns (uint256) { return store.darknodeBond(_darknodeID); } /// @notice Retrieves the encryption public key of the darknode. /// @param _darknodeID The ID of the darknode to retrieve the public key for. function getDarknodePublicKey(address _darknodeID) external view returns (bytes) { return store.darknodePublicKey(_darknodeID); } /// @notice Retrieves a list of darknodes which are registered for the /// current epoch. /// @param _start A darknode ID used as an offset for the list. If _start is /// 0x0, the first dark node will be used. _start won't be /// included it is not registered for the epoch. /// @param _count The number of darknodes to retrieve starting from _start. /// If _count is 0, all of the darknodes from _start are /// retrieved. If _count is more than the remaining number of /// registered darknodes, the rest of the list will contain /// 0x0s. function getDarknodes(address _start, uint256 _count) external view returns (address[]) { uint256 count = _count; if (count == 0) { count = numDarknodes; } return getDarknodesFromEpochs(_start, count, false); } /// @notice Retrieves a list of darknodes which were registered for the /// previous epoch. See `getDarknodes` for the parameter documentation. function getPreviousDarknodes(address _start, uint256 _count) external view returns (address[]) { uint256 count = _count; if (count == 0) { count = numDarknodesPreviousEpoch; } return getDarknodesFromEpochs(_start, count, true); } /// @notice Returns whether a darknode is scheduled to become registered /// at next epoch. /// @param _darknodeID The ID of the darknode to return function isPendingRegistration(address _darknodeID) external view returns (bool) { uint256 registeredAt = store.darknodeRegisteredAt(_darknodeID); return registeredAt != 0 && registeredAt > currentEpoch.blocknumber; } /// @notice Returns if a darknode is in the pending deregistered state. In /// this state a darknode is still considered registered. function isPendingDeregistration(address _darknodeID) external view returns (bool) { uint256 deregisteredAt = store.darknodeDeregisteredAt(_darknodeID); return deregisteredAt != 0 && deregisteredAt > currentEpoch.blocknumber; } /// @notice Returns if a darknode is in the deregistered state. function isDeregistered(address _darknodeID) public view returns (bool) { uint256 deregisteredAt = store.darknodeDeregisteredAt(_darknodeID); return deregisteredAt != 0 && deregisteredAt <= currentEpoch.blocknumber; } /// @notice Returns if a darknode can be deregistered. This is true if the /// darknodes is in the registered state and has not attempted to /// deregister yet. function isDeregisterable(address _darknodeID) public view returns (bool) { uint256 deregisteredAt = store.darknodeDeregisteredAt(_darknodeID); // The Darknode is currently in the registered state and has not been // transitioned to the pending deregistration, or deregistered, state return isRegistered(_darknodeID) && deregisteredAt == 0; } /// @notice Returns if a darknode is in the refunded state. This is true /// for darknodes that have never been registered, or darknodes that have /// been deregistered and refunded. function isRefunded(address _darknodeID) public view returns (bool) { uint256 registeredAt = store.darknodeRegisteredAt(_darknodeID); uint256 deregisteredAt = store.darknodeDeregisteredAt(_darknodeID); return registeredAt == 0 && deregisteredAt == 0; } /// @notice Returns if a darknode is refundable. This is true for darknodes /// that have been in the deregistered state for one full epoch. function isRefundable(address _darknodeID) public view returns (bool) { return isDeregistered(_darknodeID) && store.darknodeDeregisteredAt(_darknodeID) <= previousEpoch.blocknumber; } /// @notice Returns if a darknode is in the registered state. function isRegistered(address _darknodeID) public view returns (bool) { return isRegisteredInEpoch(_darknodeID, currentEpoch); } /// @notice Returns if a darknode was in the registered state last epoch. function isRegisteredInPreviousEpoch(address _darknodeID) public view returns (bool) { return isRegisteredInEpoch(_darknodeID, previousEpoch); } /// @notice Returns if a darknode was in the registered state for a given /// epoch. /// @param _darknodeID The ID of the darknode /// @param _epoch One of currentEpoch, previousEpoch function isRegisteredInEpoch(address _darknodeID, Epoch _epoch) private view returns (bool) { uint256 registeredAt = store.darknodeRegisteredAt(_darknodeID); uint256 deregisteredAt = store.darknodeDeregisteredAt(_darknodeID); bool registered = registeredAt != 0 && registeredAt <= _epoch.blocknumber; bool notDeregistered = deregisteredAt == 0 || deregisteredAt > _epoch.blocknumber; // The Darknode has been registered and has not yet been deregistered, // although it might be pending deregistration return registered && notDeregistered; } /// @notice Returns a list of darknodes registered for either the current /// or the previous epoch. See `getDarknodes` for documentation on the /// parameters `_start` and `_count`. /// @param _usePreviousEpoch If true, use the previous epoch, otherwise use /// the current epoch. function getDarknodesFromEpochs(address _start, uint256 _count, bool _usePreviousEpoch) private view returns (address[]) { uint256 count = _count; if (count == 0) { count = numDarknodes; } address[] memory nodes = new address[](count); // Begin with the first node in the list uint256 n = 0; address next = _start; if (next == 0x0) { next = store.begin(); } // Iterate until all registered Darknodes have been collected while (n < count) { if (next == 0x0) { break; } // Only include Darknodes that are currently registered bool includeNext; if (_usePreviousEpoch) { includeNext = isRegisteredInPreviousEpoch(next); } else { includeNext = isRegistered(next); } if (!includeNext) { next = store.next(next); continue; } nodes[n] = next; next = store.next(next); n += 1; } return nodes; } } /** * @title Math * @dev Assorted math operations */ library Math { function max64(uint64 a, uint64 b) internal pure returns (uint64) { return a >= b ? a : b; } function min64(uint64 a, uint64 b) internal pure returns (uint64) { return a < b ? a : b; } function max256(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } function min256(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } } /// @notice Implements safeTransfer, safeTransferFrom and /// safeApprove for CompatibleERC20. /// /// See https://github.com/ethereum/solidity/issues/4116 /// /// This library allows interacting with ERC20 tokens that implement any of /// these interfaces: /// /// (1) transfer returns true on success, false on failure /// (2) transfer returns true on success, reverts on failure /// (3) transfer returns nothing on success, reverts on failure /// /// Additionally, safeTransferFromWithFees will return the final token /// value received after accounting for token fees. library CompatibleERC20Functions { using SafeMath for uint256; /// @notice Calls transfer on the token and reverts if the call fails. function safeTransfer(address token, address to, uint256 amount) internal { CompatibleERC20(token).transfer(to, amount); require(previousReturnValue(), "transfer failed"); } /// @notice Calls transferFrom on the token and reverts if the call fails. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { CompatibleERC20(token).transferFrom(from, to, amount); require(previousReturnValue(), "transferFrom failed"); } /// @notice Calls approve on the token and reverts if the call fails. function safeApprove(address token, address spender, uint256 amount) internal { CompatibleERC20(token).approve(spender, amount); require(previousReturnValue(), "approve failed"); } /// @notice Calls transferFrom on the token, reverts if the call fails and /// returns the value transferred after fees. function safeTransferFromWithFees(address token, address from, address to, uint256 amount) internal returns (uint256) { uint256 balancesBefore = CompatibleERC20(token).balanceOf(to); CompatibleERC20(token).transferFrom(from, to, amount); require(previousReturnValue(), "transferFrom failed"); uint256 balancesAfter = CompatibleERC20(token).balanceOf(to); return Math.min256(amount, balancesAfter.sub(balancesBefore)); } /// @notice Checks the return value of the previous function. Returns true /// if the previous function returned 32 non-zero bytes or returned zero /// bytes. function previousReturnValue() private pure returns (bool) { uint256 returnData = 0; assembly { /* solium-disable-line security/no-inline-assembly */ // Switch on the number of bytes returned by the previous call switch returndatasize // 0 bytes: ERC20 of type (3), did not throw case 0 { returnData := 1 } // 32 bytes: ERC20 of types (1) or (2) case 32 { // Copy the return data into scratch space returndatacopy(0x0, 0x0, 32) // Load the return data into returnData returnData := mload(0x0) } // Other return size: return false default { } } return returnData != 0; } } /// @notice ERC20 interface which doesn't specify the return type for transfer, /// transferFrom and approve. interface CompatibleERC20 { // Modified to not return boolean function transfer(address to, uint256 value) external; function transferFrom(address from, address to, uint256 value) external; function approve(address spender, uint256 value) external; // Not modifier function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } /// @notice The DarknodeRewardVault contract is responsible for holding fees /// for darknodes for settling orders. Fees can be withdrawn to the address of /// the darknode's operator. Fees can be in ETH or in ERC20 tokens. /// Docs: https://github.com/republicprotocol/republic-sol/blob/master/docs/02-darknode-reward-vault.md contract DarknodeRewardVault is Ownable { using SafeMath for uint256; using CompatibleERC20Functions for CompatibleERC20; string public VERSION; // Passed in as a constructor parameter. /// @notice The special address for Ether. address constant public ETHEREUM = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; DarknodeRegistry public darknodeRegistry; mapping(address => mapping(address => uint256)) public darknodeBalances; event LogDarknodeRegistryUpdated(DarknodeRegistry previousDarknodeRegistry, DarknodeRegistry nextDarknodeRegistry); /// @notice The contract constructor. /// /// @param _VERSION A string defining the contract version. /// @param _darknodeRegistry The DarknodeRegistry contract that is used by /// the vault to lookup Darknode owners. constructor(string _VERSION, DarknodeRegistry _darknodeRegistry) public { VERSION = _VERSION; darknodeRegistry = _darknodeRegistry; } function updateDarknodeRegistry(DarknodeRegistry _newDarknodeRegistry) public onlyOwner { emit LogDarknodeRegistryUpdated(darknodeRegistry, _newDarknodeRegistry); darknodeRegistry = _newDarknodeRegistry; } /// @notice Deposit fees into the vault for a Darknode. The Darknode /// registration is not checked (to reduce gas fees); the caller must be /// careful not to call this function for a Darknode that is not registered /// otherwise any fees deposited to that Darknode can be withdrawn by a /// malicious adversary (by registering the Darknode before the honest /// party and claiming ownership). /// /// @param _darknode The address of the Darknode that will receive the /// fees. /// @param _token The address of the ERC20 token being used to pay the fee. /// A special address is used for Ether. /// @param _value The amount of fees in the smallest unit of the token. function deposit(address _darknode, ERC20 _token, uint256 _value) public payable { uint256 receivedValue = _value; if (address(_token) == ETHEREUM) { require(msg.value == _value, "mismatched ether value"); } else { require(msg.value == 0, "unexpected ether value"); receivedValue = CompatibleERC20(_token).safeTransferFromWithFees(msg.sender, address(this), _value); } darknodeBalances[_darknode][_token] = darknodeBalances[_darknode][_token].add(receivedValue); } /// @notice Withdraw fees earned by a Darknode. The fees will be sent to /// the owner of the Darknode. If a Darknode is not registered the fees /// cannot be withdrawn. /// /// @param _darknode The address of the Darknode whose fees are being /// withdrawn. The owner of this Darknode will receive the fees. /// @param _token The address of the ERC20 token to withdraw. function withdraw(address _darknode, ERC20 _token) public { address darknodeOwner = darknodeRegistry.getDarknodeOwner(address(_darknode)); require(darknodeOwner != 0x0, "invalid darknode owner"); uint256 value = darknodeBalances[_darknode][_token]; darknodeBalances[_darknode][_token] = 0; if (address(_token) == ETHEREUM) { darknodeOwner.transfer(value); } else { CompatibleERC20(_token).safeTransfer(darknodeOwner, value); } } } /// @notice The BrokerVerifier interface defines the functions that a settlement /// layer's broker verifier contract must implement. interface BrokerVerifier { /// @notice The function signature that will be called when a trader opens /// an order. /// /// @param _trader The trader requesting the withdrawal. /// @param _signature The 65-byte signature from the broker. /// @param _orderID The 32-byte order ID. function verifyOpenSignature( address _trader, bytes _signature, bytes32 _orderID ) external returns (bool); } /// @notice The Settlement interface defines the functions that a settlement /// layer must implement. /// Docs: https://github.com/republicprotocol/republic-sol/blob/nightly/docs/05-settlement.md interface Settlement { function submitOrder( bytes _details, uint64 _settlementID, uint64 _tokens, uint256 _price, uint256 _volume, uint256 _minimumVolume ) external; function submissionGasPriceLimit() external view returns (uint256); function settle( bytes32 _buyID, bytes32 _sellID ) external; /// @notice orderStatus should return the status of the order, which should /// be: /// 0 - Order not seen before /// 1 - Order details submitted /// >1 - Order settled, or settlement no longer possible function orderStatus(bytes32 _orderID) external view returns (uint8); } /// @notice SettlementRegistry allows a Settlement layer to register the /// contracts used for match settlement and for broker signature verification. contract SettlementRegistry is Ownable { string public VERSION; // Passed in as a constructor parameter. struct SettlementDetails { bool registered; Settlement settlementContract; BrokerVerifier brokerVerifierContract; } // Settlement IDs are 64-bit unsigned numbers mapping(uint64 => SettlementDetails) public settlementDetails; // Events event LogSettlementRegistered(uint64 settlementID, Settlement settlementContract, BrokerVerifier brokerVerifierContract); event LogSettlementUpdated(uint64 settlementID, Settlement settlementContract, BrokerVerifier brokerVerifierContract); event LogSettlementDeregistered(uint64 settlementID); /// @notice The contract constructor. /// /// @param _VERSION A string defining the contract version. constructor(string _VERSION) public { VERSION = _VERSION; } /// @notice Returns the settlement contract of a settlement layer. function settlementRegistration(uint64 _settlementID) external view returns (bool) { return settlementDetails[_settlementID].registered; } /// @notice Returns the settlement contract of a settlement layer. function settlementContract(uint64 _settlementID) external view returns (Settlement) { return settlementDetails[_settlementID].settlementContract; } /// @notice Returns the broker verifier contract of a settlement layer. function brokerVerifierContract(uint64 _settlementID) external view returns (BrokerVerifier) { return settlementDetails[_settlementID].brokerVerifierContract; } /// @param _settlementID A unique 64-bit settlement identifier. /// @param _settlementContract The address to use for settling matches. /// @param _brokerVerifierContract The decimals to use for verifying /// broker signatures. function registerSettlement(uint64 _settlementID, Settlement _settlementContract, BrokerVerifier _brokerVerifierContract) public onlyOwner { bool alreadyRegistered = settlementDetails[_settlementID].registered; settlementDetails[_settlementID] = SettlementDetails({ registered: true, settlementContract: _settlementContract, brokerVerifierContract: _brokerVerifierContract }); if (alreadyRegistered) { emit LogSettlementUpdated(_settlementID, _settlementContract, _brokerVerifierContract); } else { emit LogSettlementRegistered(_settlementID, _settlementContract, _brokerVerifierContract); } } /// @notice Deregisteres a settlement layer, clearing the details. /// @param _settlementID The unique 64-bit settlement identifier. function deregisterSettlement(uint64 _settlementID) external onlyOwner { require(settlementDetails[_settlementID].registered, "not registered"); delete settlementDetails[_settlementID]; emit LogSettlementDeregistered(_settlementID); } } /** * @title Eliptic curve signature operations * @dev Based on https://gist.github.com/axic/5b33912c6f61ae6fd96d6c4a47afde6d * TODO Remove this library once solidity supports passing a signature to ecrecover. * See https://github.com/ethereum/solidity/issues/864 */ library ECRecovery { /** * @dev Recover signer address from a message by using their signature * @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address. * @param sig bytes signature, the signature is generated using web3.eth.sign() */ function recover(bytes32 hash, bytes sig) internal pure returns (address) { bytes32 r; bytes32 s; uint8 v; // Check the signature length if (sig.length != 65) { return (address(0)); } // Divide the signature in r, s and v variables // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solium-disable-next-line security/no-inline-assembly assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) } // Version of signature should be 27 or 28, but 0 and 1 are also possible versions if (v < 27) { v += 27; } // If the version is correct return the signer address if (v != 27 && v != 28) { return (address(0)); } else { // solium-disable-next-line arg-overflow return ecrecover(hash, v, r, s); } } /** * toEthSignedMessageHash * @dev prefix a bytes32 value with "\x19Ethereum Signed Message:" * and hash the result */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256( abi.encodePacked("\x19Ethereum Signed Message:\n32", hash) ); } } library Utils { /** * @notice Converts a number to its string/bytes representation * * @param _v the uint to convert */ function uintToBytes(uint256 _v) internal pure returns (bytes) { uint256 v = _v; if (v == 0) { return "0"; } uint256 digits = 0; uint256 v2 = v; while (v2 > 0) { v2 /= 10; digits += 1; } bytes memory result = new bytes(digits); for (uint256 i = 0; i < digits; i++) { result[digits - i - 1] = bytes1((v % 10) + 48); v /= 10; } return result; } /** * @notice Retrieves the address from a signature * * @param _hash the message that was signed (any length of bytes) * @param _signature the signature (65 bytes) */ function addr(bytes _hash, bytes _signature) internal pure returns (address) { bytes memory prefix = "\x19Ethereum Signed Message:\n"; bytes memory encoded = abi.encodePacked(prefix, uintToBytes(_hash.length), _hash); bytes32 prefixedHash = keccak256(encoded); return ECRecovery.recover(prefixedHash, _signature); } } /// @notice The Orderbook contract stores the state and priority of orders and /// allows the Darknodes to easily reach consensus. Eventually, this contract /// will only store a subset of order states, such as cancellation, to improve /// the throughput of orders. contract Orderbook is Ownable { string public VERSION; // Passed in as a constructor parameter. /// @notice OrderState enumerates the possible states of an order. All /// orders default to the Undefined state. enum OrderState {Undefined, Open, Confirmed, Canceled} /// @notice Order stores a subset of the public data associated with an order. struct Order { OrderState state; // State of the order address trader; // Trader that owns the order address confirmer; // Darknode that confirmed the order in a match uint64 settlementID; // The settlement that signed the order opening uint256 priority; // Logical time priority of this order uint256 blockNumber; // Block number of the most recent state change bytes32 matchedOrder; // Order confirmed in a match with this order } DarknodeRegistry public darknodeRegistry; SettlementRegistry public settlementRegistry; bytes32[] private orderbook; // Order details are exposed through directly accessing this mapping, or // through the getter functions below for each of the order's fields. mapping(bytes32 => Order) public orders; event LogFeeUpdated(uint256 previousFee, uint256 nextFee); event LogDarknodeRegistryUpdated(DarknodeRegistry previousDarknodeRegistry, DarknodeRegistry nextDarknodeRegistry); /// @notice Only allow registered dark nodes. modifier onlyDarknode(address _sender) { require(darknodeRegistry.isRegistered(address(_sender)), "must be registered darknode"); _; } /// @notice The contract constructor. /// /// @param _VERSION A string defining the contract version. /// @param _darknodeRegistry The address of the DarknodeRegistry contract. /// @param _settlementRegistry The address of the SettlementRegistry /// contract. constructor( string _VERSION, DarknodeRegistry _darknodeRegistry, SettlementRegistry _settlementRegistry ) public { VERSION = _VERSION; darknodeRegistry = _darknodeRegistry; settlementRegistry = _settlementRegistry; } /// @notice Allows the owner to update the address of the DarknodeRegistry /// contract. function updateDarknodeRegistry(DarknodeRegistry _newDarknodeRegistry) external onlyOwner { emit LogDarknodeRegistryUpdated(darknodeRegistry, _newDarknodeRegistry); darknodeRegistry = _newDarknodeRegistry; } /// @notice Open an order in the orderbook. The order must be in the /// Undefined state. /// /// @param _signature Signature of the message that defines the trader. The /// message is "Republic Protocol: open: {orderId}". /// @param _orderID The hash of the order. function openOrder(uint64 _settlementID, bytes _signature, bytes32 _orderID) external { require(orders[_orderID].state == OrderState.Undefined, "invalid order status"); address trader = msg.sender; // Verify the order signature require(settlementRegistry.settlementRegistration(_settlementID), "settlement not registered"); BrokerVerifier brokerVerifier = settlementRegistry.brokerVerifierContract(_settlementID); require(brokerVerifier.verifyOpenSignature(trader, _signature, _orderID), "invalid broker signature"); orders[_orderID] = Order({ state: OrderState.Open, trader: trader, confirmer: 0x0, settlementID: _settlementID, priority: orderbook.length + 1, blockNumber: block.number, matchedOrder: 0x0 }); orderbook.push(_orderID); } /// @notice Confirm an order match between orders. The confirmer must be a /// registered Darknode and the orders must be in the Open state. A /// malicious confirmation by a Darknode will result in a bond slash of the /// Darknode. /// /// @param _orderID The hash of the order. /// @param _matchedOrderID The hashes of the matching order. function confirmOrder(bytes32 _orderID, bytes32 _matchedOrderID) external onlyDarknode(msg.sender) { require(orders[_orderID].state == OrderState.Open, "invalid order status"); require(orders[_matchedOrderID].state == OrderState.Open, "invalid order status"); orders[_orderID].state = OrderState.Confirmed; orders[_orderID].confirmer = msg.sender; orders[_orderID].matchedOrder = _matchedOrderID; orders[_orderID].blockNumber = block.number; orders[_matchedOrderID].state = OrderState.Confirmed; orders[_matchedOrderID].confirmer = msg.sender; orders[_matchedOrderID].matchedOrder = _orderID; orders[_matchedOrderID].blockNumber = block.number; } /// @notice Cancel an open order in the orderbook. An order can be cancelled /// by the trader who opened the order, or by the broker verifier contract. /// This allows the settlement layer to implement their own logic for /// cancelling orders without trader interaction (e.g. to ban a trader from /// a specific darkpool, or to use multiple order-matching platforms) /// /// @param _orderID The hash of the order. function cancelOrder(bytes32 _orderID) external { require(orders[_orderID].state == OrderState.Open, "invalid order state"); // Require the msg.sender to be the trader or the broker verifier address brokerVerifier = address(settlementRegistry.brokerVerifierContract(orders[_orderID].settlementID)); require(msg.sender == orders[_orderID].trader || msg.sender == brokerVerifier, "not authorized"); orders[_orderID].state = OrderState.Canceled; orders[_orderID].blockNumber = block.number; } /// @notice returns status of the given orderID. function orderState(bytes32 _orderID) external view returns (OrderState) { return orders[_orderID].state; } /// @notice returns a list of matched orders to the given orderID. function orderMatch(bytes32 _orderID) external view returns (bytes32) { return orders[_orderID].matchedOrder; } /// @notice returns the priority of the given orderID. /// The priority is the index of the order in the orderbook. function orderPriority(bytes32 _orderID) external view returns (uint256) { return orders[_orderID].priority; } /// @notice returns the trader of the given orderID. /// Trader is the one who signs the message and does the actual trading. function orderTrader(bytes32 _orderID) external view returns (address) { return orders[_orderID].trader; } /// @notice returns the darknode address which confirms the given orderID. function orderConfirmer(bytes32 _orderID) external view returns (address) { return orders[_orderID].confirmer; } /// @notice returns the block number when the order being last modified. function orderBlockNumber(bytes32 _orderID) external view returns (uint256) { return orders[_orderID].blockNumber; } /// @notice returns the block depth of the orderId function orderDepth(bytes32 _orderID) external view returns (uint256) { if (orders[_orderID].blockNumber == 0) { return 0; } return (block.number - orders[_orderID].blockNumber); } /// @notice returns the number of orders in the orderbook function ordersCount() external view returns (uint256) { return orderbook.length; } /// @notice returns order details of the orders starting from the offset. function getOrders(uint256 _offset, uint256 _limit) external view returns (bytes32[], address[], uint8[]) { if (_offset >= orderbook.length) { return; } // If the provided limit is more than the number of orders after the offset, // decrease the limit uint256 limit = _limit; if (_offset + limit > orderbook.length) { limit = orderbook.length - _offset; } bytes32[] memory orderIDs = new bytes32[](limit); address[] memory traderAddresses = new address[](limit); uint8[] memory states = new uint8[](limit); for (uint256 i = 0; i < limit; i++) { bytes32 order = orderbook[i + _offset]; orderIDs[i] = order; traderAddresses[i] = orders[order].trader; states[i] = uint8(orders[order].state); } return (orderIDs, traderAddresses, states); } } /// @notice A library for calculating and verifying order match details library SettlementUtils { struct OrderDetails { uint64 settlementID; uint64 tokens; uint256 price; uint256 volume; uint256 minimumVolume; } /// @notice Calculates the ID of the order. /// @param details Order details that are not required for settlement /// execution. They are combined as a single byte array. /// @param order The order details required for settlement execution. function hashOrder(bytes details, OrderDetails memory order) internal pure returns (bytes32) { return keccak256( abi.encodePacked( details, order.settlementID, order.tokens, order.price, order.volume, order.minimumVolume ) ); } /// @notice Verifies that two orders match when considering the tokens, /// price, volumes / minimum volumes and settlement IDs. verifyMatchDetails is used /// my the DarknodeSlasher to verify challenges. Settlement layers may also /// use this function. /// @dev When verifying two orders for settlement, you should also: /// 1) verify the orders have been confirmed together /// 2) verify the orders' traders are distinct /// @param _buy The buy order details. /// @param _sell The sell order details. function verifyMatchDetails(OrderDetails memory _buy, OrderDetails memory _sell) internal pure returns (bool) { // Buy and sell tokens should match if (!verifyTokens(_buy.tokens, _sell.tokens)) { return false; } // Buy price should be greater than sell price if (_buy.price < _sell.price) { return false; } // // Buy volume should be greater than sell minimum volume if (_buy.volume < _sell.minimumVolume) { return false; } // Sell volume should be greater than buy minimum volume if (_sell.volume < _buy.minimumVolume) { return false; } // Require that the orders were submitted to the same settlement layer if (_buy.settlementID != _sell.settlementID) { return false; } return true; } /// @notice Verifies that two token requirements can be matched and that the /// tokens are formatted correctly. /// @param _buyTokens The buy token details. /// @param _sellToken The sell token details. function verifyTokens(uint64 _buyTokens, uint64 _sellToken) internal pure returns (bool) { return (( uint32(_buyTokens) == uint32(_sellToken >> 32)) && ( uint32(_sellToken) == uint32(_buyTokens >> 32)) && ( uint32(_buyTokens >> 32) <= uint32(_buyTokens)) ); } } /// @notice RenExTokens is a registry of tokens that can be traded on RenEx. contract RenExTokens is Ownable { string public VERSION; // Passed in as a constructor parameter. struct TokenDetails { address addr; uint8 decimals; bool registered; } // Storage mapping(uint32 => TokenDetails) public tokens; mapping(uint32 => bool) private detailsSubmitted; // Events event LogTokenRegistered(uint32 tokenCode, address tokenAddress, uint8 tokenDecimals); event LogTokenDeregistered(uint32 tokenCode); /// @notice The contract constructor. /// /// @param _VERSION A string defining the contract version. constructor(string _VERSION) public { VERSION = _VERSION; } /// @notice Allows the owner to register and the details for a token. /// Once details have been submitted, they cannot be overwritten. /// To re-register the same token with different details (e.g. if the address /// has changed), a different token identifier should be used and the /// previous token identifier should be deregistered. /// If a token is not Ethereum-based, the address will be set to 0x0. /// /// @param _tokenCode A unique 32-bit token identifier. /// @param _tokenAddress The address of the token. /// @param _tokenDecimals The decimals to use for the token. function registerToken(uint32 _tokenCode, address _tokenAddress, uint8 _tokenDecimals) public onlyOwner { require(!tokens[_tokenCode].registered, "already registered"); // If a token is being re-registered, the same details must be provided. if (detailsSubmitted[_tokenCode]) { require(tokens[_tokenCode].addr == _tokenAddress, "different address"); require(tokens[_tokenCode].decimals == _tokenDecimals, "different decimals"); } else { detailsSubmitted[_tokenCode] = true; } tokens[_tokenCode] = TokenDetails({ addr: _tokenAddress, decimals: _tokenDecimals, registered: true }); emit LogTokenRegistered(_tokenCode, _tokenAddress, _tokenDecimals); } /// @notice Sets a token as being deregistered. The details are still stored /// to prevent the token from being re-registered with different details. /// /// @param _tokenCode The unique 32-bit token identifier. function deregisterToken(uint32 _tokenCode) external onlyOwner { require(tokens[_tokenCode].registered, "not registered"); tokens[_tokenCode].registered = false; emit LogTokenDeregistered(_tokenCode); } } /// @notice RenExSettlement implements the Settlement interface. It implements /// the on-chain settlement for the RenEx settlement layer, and the fee payment /// for the RenExAtomic settlement layer. contract RenExSettlement is Ownable { using SafeMath for uint256; string public VERSION; // Passed in as a constructor parameter. // This contract handles the settlements with ID 1 and 2. uint32 constant public RENEX_SETTLEMENT_ID = 1; uint32 constant public RENEX_ATOMIC_SETTLEMENT_ID = 2; // Fees in RenEx are 0.2%. To represent this as integers, it is broken into // a numerator and denominator. uint256 constant public DARKNODE_FEES_NUMERATOR = 2; uint256 constant public DARKNODE_FEES_DENOMINATOR = 1000; // Constants used in the price / volume inputs. int16 constant private PRICE_OFFSET = 12; int16 constant private VOLUME_OFFSET = 12; // Constructor parameters, updatable by the owner Orderbook public orderbookContract; RenExTokens public renExTokensContract; RenExBalances public renExBalancesContract; address public slasherAddress; uint256 public submissionGasPriceLimit; enum OrderStatus {None, Submitted, Settled, Slashed} struct TokenPair { RenExTokens.TokenDetails priorityToken; RenExTokens.TokenDetails secondaryToken; } // A uint256 tuple representing a value and an associated fee struct ValueWithFees { uint256 value; uint256 fees; } // A uint256 tuple representing a fraction struct Fraction { uint256 numerator; uint256 denominator; } // We use left and right because the tokens do not always represent the // priority and secondary tokens. struct SettlementDetails { uint256 leftVolume; uint256 rightVolume; uint256 leftTokenFee; uint256 rightTokenFee; address leftTokenAddress; address rightTokenAddress; } // Events event LogOrderbookUpdated(Orderbook previousOrderbook, Orderbook nextOrderbook); event LogRenExTokensUpdated(RenExTokens previousRenExTokens, RenExTokens nextRenExTokens); event LogRenExBalancesUpdated(RenExBalances previousRenExBalances, RenExBalances nextRenExBalances); event LogSubmissionGasPriceLimitUpdated(uint256 previousSubmissionGasPriceLimit, uint256 nextSubmissionGasPriceLimit); event LogSlasherUpdated(address previousSlasher, address nextSlasher); // Order Storage mapping(bytes32 => SettlementUtils.OrderDetails) public orderDetails; mapping(bytes32 => address) public orderSubmitter; mapping(bytes32 => OrderStatus) public orderStatus; // Match storage (match details are indexed by [buyID][sellID]) mapping(bytes32 => mapping(bytes32 => uint256)) public matchTimestamp; /// @notice Prevents a function from being called with a gas price higher /// than the specified limit. /// /// @param _gasPriceLimit The gas price upper-limit in Wei. modifier withGasPriceLimit(uint256 _gasPriceLimit) { require(tx.gasprice <= _gasPriceLimit, "gas price too high"); _; } /// @notice Restricts a function to only being called by the slasher /// address. modifier onlySlasher() { require(msg.sender == slasherAddress, "unauthorized"); _; } /// @notice The contract constructor. /// /// @param _VERSION A string defining the contract version. /// @param _orderbookContract The address of the Orderbook contract. /// @param _renExBalancesContract The address of the RenExBalances /// contract. /// @param _renExTokensContract The address of the RenExTokens contract. constructor( string _VERSION, Orderbook _orderbookContract, RenExTokens _renExTokensContract, RenExBalances _renExBalancesContract, address _slasherAddress, uint256 _submissionGasPriceLimit ) public { VERSION = _VERSION; orderbookContract = _orderbookContract; renExTokensContract = _renExTokensContract; renExBalancesContract = _renExBalancesContract; slasherAddress = _slasherAddress; submissionGasPriceLimit = _submissionGasPriceLimit; } /// @notice The owner of the contract can update the Orderbook address. /// @param _newOrderbookContract The address of the new Orderbook contract. function updateOrderbook(Orderbook _newOrderbookContract) external onlyOwner { emit LogOrderbookUpdated(orderbookContract, _newOrderbookContract); orderbookContract = _newOrderbookContract; } /// @notice The owner of the contract can update the RenExTokens address. /// @param _newRenExTokensContract The address of the new RenExTokens /// contract. function updateRenExTokens(RenExTokens _newRenExTokensContract) external onlyOwner { emit LogRenExTokensUpdated(renExTokensContract, _newRenExTokensContract); renExTokensContract = _newRenExTokensContract; } /// @notice The owner of the contract can update the RenExBalances address. /// @param _newRenExBalancesContract The address of the new RenExBalances /// contract. function updateRenExBalances(RenExBalances _newRenExBalancesContract) external onlyOwner { emit LogRenExBalancesUpdated(renExBalancesContract, _newRenExBalancesContract); renExBalancesContract = _newRenExBalancesContract; } /// @notice The owner of the contract can update the order submission gas /// price limit. /// @param _newSubmissionGasPriceLimit The new gas price limit. function updateSubmissionGasPriceLimit(uint256 _newSubmissionGasPriceLimit) external onlyOwner { emit LogSubmissionGasPriceLimitUpdated(submissionGasPriceLimit, _newSubmissionGasPriceLimit); submissionGasPriceLimit = _newSubmissionGasPriceLimit; } /// @notice The owner of the contract can update the slasher address. /// @param _newSlasherAddress The new slasher address. function updateSlasher(address _newSlasherAddress) external onlyOwner { emit LogSlasherUpdated(slasherAddress, _newSlasherAddress); slasherAddress = _newSlasherAddress; } /// @notice Stores the details of an order. /// /// @param _prefix The miscellaneous details of the order required for /// calculating the order id. /// @param _settlementID The settlement identifier. /// @param _tokens The encoding of the token pair (buy token is encoded as /// the first 32 bytes and sell token is encoded as the last 32 /// bytes). /// @param _price The price of the order. Interpreted as the cost for 1 /// standard unit of the non-priority token, in 1e12 (i.e. /// PRICE_OFFSET) units of the priority token). /// @param _volume The volume of the order. Interpreted as the maximum /// number of 1e-12 (i.e. VOLUME_OFFSET) units of the non-priority /// token that can be traded by this order. /// @param _minimumVolume The minimum volume the trader is willing to /// accept. Encoded the same as the volume. function submitOrder( bytes _prefix, uint64 _settlementID, uint64 _tokens, uint256 _price, uint256 _volume, uint256 _minimumVolume ) external withGasPriceLimit(submissionGasPriceLimit) { SettlementUtils.OrderDetails memory order = SettlementUtils.OrderDetails({ settlementID: _settlementID, tokens: _tokens, price: _price, volume: _volume, minimumVolume: _minimumVolume }); bytes32 orderID = SettlementUtils.hashOrder(_prefix, order); require(orderStatus[orderID] == OrderStatus.None, "order already submitted"); require(orderbookContract.orderState(orderID) == Orderbook.OrderState.Confirmed, "unconfirmed order"); orderSubmitter[orderID] = msg.sender; orderStatus[orderID] = OrderStatus.Submitted; orderDetails[orderID] = order; } /// @notice Settles two orders that are matched. `submitOrder` must have been /// called for each order before this function is called. /// /// @param _buyID The 32 byte ID of the buy order. /// @param _sellID The 32 byte ID of the sell order. function settle(bytes32 _buyID, bytes32 _sellID) external { require(orderStatus[_buyID] == OrderStatus.Submitted, "invalid buy status"); require(orderStatus[_sellID] == OrderStatus.Submitted, "invalid sell status"); // Check the settlement ID (only have to check for one, since // `verifyMatchDetails` checks that they are the same) require( orderDetails[_buyID].settlementID == RENEX_ATOMIC_SETTLEMENT_ID || orderDetails[_buyID].settlementID == RENEX_SETTLEMENT_ID, "invalid settlement id" ); // Verify that the two order details are compatible. require(SettlementUtils.verifyMatchDetails(orderDetails[_buyID], orderDetails[_sellID]), "incompatible orders"); // Verify that the two orders have been confirmed to one another. require(orderbookContract.orderMatch(_buyID) == _sellID, "unconfirmed orders"); // Retrieve token details. TokenPair memory tokens = getTokenDetails(orderDetails[_buyID].tokens); // Require that the tokens have been registered. require(tokens.priorityToken.registered, "unregistered priority token"); require(tokens.secondaryToken.registered, "unregistered secondary token"); address buyer = orderbookContract.orderTrader(_buyID); address seller = orderbookContract.orderTrader(_sellID); require(buyer != seller, "orders from same trader"); execute(_buyID, _sellID, buyer, seller, tokens); /* solium-disable-next-line security/no-block-members */ matchTimestamp[_buyID][_sellID] = now; // Store that the orders have been settled. orderStatus[_buyID] = OrderStatus.Settled; orderStatus[_sellID] = OrderStatus.Settled; } /// @notice Slashes the bond of a guilty trader. This is called when an /// atomic swap is not executed successfully. /// To open an atomic order, a trader must have a balance equivalent to /// 0.6% of the trade in the Ethereum-based token. 0.2% is always paid in /// darknode fees when the order is matched. If the remaining amount is /// is slashed, it is distributed as follows: /// 1) 0.2% goes to the other trader, covering their fee /// 2) 0.2% goes to the slasher address /// Only one order in a match can be slashed. /// /// @param _guiltyOrderID The 32 byte ID of the order of the guilty trader. function slash(bytes32 _guiltyOrderID) external onlySlasher { require(orderDetails[_guiltyOrderID].settlementID == RENEX_ATOMIC_SETTLEMENT_ID, "slashing non-atomic trade"); bytes32 innocentOrderID = orderbookContract.orderMatch(_guiltyOrderID); require(orderStatus[_guiltyOrderID] == OrderStatus.Settled, "invalid order status"); require(orderStatus[innocentOrderID] == OrderStatus.Settled, "invalid order status"); orderStatus[_guiltyOrderID] = OrderStatus.Slashed; (bytes32 buyID, bytes32 sellID) = isBuyOrder(_guiltyOrderID) ? (_guiltyOrderID, innocentOrderID) : (innocentOrderID, _guiltyOrderID); TokenPair memory tokens = getTokenDetails(orderDetails[buyID].tokens); SettlementDetails memory settlementDetails = calculateAtomicFees(buyID, sellID, tokens); // Transfer the fee amount to the other trader renExBalancesContract.transferBalanceWithFee( orderbookContract.orderTrader(_guiltyOrderID), orderbookContract.orderTrader(innocentOrderID), settlementDetails.leftTokenAddress, settlementDetails.leftTokenFee, 0, 0x0 ); // Transfer the fee amount to the slasher renExBalancesContract.transferBalanceWithFee( orderbookContract.orderTrader(_guiltyOrderID), slasherAddress, settlementDetails.leftTokenAddress, settlementDetails.leftTokenFee, 0, 0x0 ); } /// @notice Retrieves the settlement details of an order. /// For atomic swaps, it returns the full volumes, not the settled fees. /// /// @param _orderID The order to lookup the details of. Can be the ID of a /// buy or a sell order. /// @return [ /// a boolean representing whether or not the order has been settled, /// a boolean representing whether or not the order is a buy /// the 32-byte order ID of the matched order /// the volume of the priority token, /// the volume of the secondary token, /// the fee paid in the priority token, /// the fee paid in the secondary token, /// the token code of the priority token, /// the token code of the secondary token /// ] function getMatchDetails(bytes32 _orderID) external view returns ( bool settled, bool orderIsBuy, bytes32 matchedID, uint256 priorityVolume, uint256 secondaryVolume, uint256 priorityFee, uint256 secondaryFee, uint32 priorityToken, uint32 secondaryToken ) { matchedID = orderbookContract.orderMatch(_orderID); orderIsBuy = isBuyOrder(_orderID); (bytes32 buyID, bytes32 sellID) = orderIsBuy ? (_orderID, matchedID) : (matchedID, _orderID); SettlementDetails memory settlementDetails = calculateSettlementDetails( buyID, sellID, getTokenDetails(orderDetails[buyID].tokens) ); return ( orderStatus[_orderID] == OrderStatus.Settled || orderStatus[_orderID] == OrderStatus.Slashed, orderIsBuy, matchedID, settlementDetails.leftVolume, settlementDetails.rightVolume, settlementDetails.leftTokenFee, settlementDetails.rightTokenFee, uint32(orderDetails[buyID].tokens >> 32), uint32(orderDetails[buyID].tokens) ); } /// @notice Exposes the hashOrder function for computing a hash of an /// order's details. An order hash is used as its ID. See `submitOrder` /// for the parameter descriptions. /// /// @return The 32-byte hash of the order. function hashOrder( bytes _prefix, uint64 _settlementID, uint64 _tokens, uint256 _price, uint256 _volume, uint256 _minimumVolume ) external pure returns (bytes32) { return SettlementUtils.hashOrder(_prefix, SettlementUtils.OrderDetails({ settlementID: _settlementID, tokens: _tokens, price: _price, volume: _volume, minimumVolume: _minimumVolume })); } /// @notice Called by `settle`, executes the settlement for a RenEx order /// or distributes the fees for a RenExAtomic swap. /// /// @param _buyID The 32 byte ID of the buy order. /// @param _sellID The 32 byte ID of the sell order. /// @param _buyer The address of the buy trader. /// @param _seller The address of the sell trader. /// @param _tokens The details of the priority and secondary tokens. function execute( bytes32 _buyID, bytes32 _sellID, address _buyer, address _seller, TokenPair memory _tokens ) private { // Calculate the fees for atomic swaps, and the settlement details // otherwise. SettlementDetails memory settlementDetails = (orderDetails[_buyID].settlementID == RENEX_ATOMIC_SETTLEMENT_ID) ? settlementDetails = calculateAtomicFees(_buyID, _sellID, _tokens) : settlementDetails = calculateSettlementDetails(_buyID, _sellID, _tokens); // Transfer priority token value renExBalancesContract.transferBalanceWithFee( _buyer, _seller, settlementDetails.leftTokenAddress, settlementDetails.leftVolume, settlementDetails.leftTokenFee, orderSubmitter[_buyID] ); // Transfer secondary token value renExBalancesContract.transferBalanceWithFee( _seller, _buyer, settlementDetails.rightTokenAddress, settlementDetails.rightVolume, settlementDetails.rightTokenFee, orderSubmitter[_sellID] ); } /// @notice Calculates the details required to execute two matched orders. /// /// @param _buyID The 32 byte ID of the buy order. /// @param _sellID The 32 byte ID of the sell order. /// @param _tokens The details of the priority and secondary tokens. /// @return A struct containing the settlement details. function calculateSettlementDetails( bytes32 _buyID, bytes32 _sellID, TokenPair memory _tokens ) private view returns (SettlementDetails memory) { // Calculate the mid-price (using numerator and denominator to not loose // precision). Fraction memory midPrice = Fraction(orderDetails[_buyID].price + orderDetails[_sellID].price, 2); // Calculate the lower of the two max volumes of each trader uint256 commonVolume = Math.min256(orderDetails[_buyID].volume, orderDetails[_sellID].volume); uint256 priorityTokenVolume = joinFraction( commonVolume.mul(midPrice.numerator), midPrice.denominator, int16(_tokens.priorityToken.decimals) - PRICE_OFFSET - VOLUME_OFFSET ); uint256 secondaryTokenVolume = joinFraction( commonVolume, 1, int16(_tokens.secondaryToken.decimals) - VOLUME_OFFSET ); // Calculate darknode fees ValueWithFees memory priorityVwF = subtractDarknodeFee(priorityTokenVolume); ValueWithFees memory secondaryVwF = subtractDarknodeFee(secondaryTokenVolume); return SettlementDetails({ leftVolume: priorityVwF.value, rightVolume: secondaryVwF.value, leftTokenFee: priorityVwF.fees, rightTokenFee: secondaryVwF.fees, leftTokenAddress: _tokens.priorityToken.addr, rightTokenAddress: _tokens.secondaryToken.addr }); } /// @notice Calculates the fees to be transferred for an atomic swap. /// /// @param _buyID The 32 byte ID of the buy order. /// @param _sellID The 32 byte ID of the sell order. /// @param _tokens The details of the priority and secondary tokens. /// @return A struct containing the fee details. function calculateAtomicFees( bytes32 _buyID, bytes32 _sellID, TokenPair memory _tokens ) private view returns (SettlementDetails memory) { // Calculate the mid-price (using numerator and denominator to not loose // precision). Fraction memory midPrice = Fraction(orderDetails[_buyID].price + orderDetails[_sellID].price, 2); // Calculate the lower of the two max volumes of each trader uint256 commonVolume = Math.min256(orderDetails[_buyID].volume, orderDetails[_sellID].volume); if (isEthereumBased(_tokens.secondaryToken.addr)) { uint256 secondaryTokenVolume = joinFraction( commonVolume, 1, int16(_tokens.secondaryToken.decimals) - VOLUME_OFFSET ); // Calculate darknode fees ValueWithFees memory secondaryVwF = subtractDarknodeFee(secondaryTokenVolume); return SettlementDetails({ leftVolume: 0, rightVolume: 0, leftTokenFee: secondaryVwF.fees, rightTokenFee: secondaryVwF.fees, leftTokenAddress: _tokens.secondaryToken.addr, rightTokenAddress: _tokens.secondaryToken.addr }); } else if (isEthereumBased(_tokens.priorityToken.addr)) { uint256 priorityTokenVolume = joinFraction( commonVolume.mul(midPrice.numerator), midPrice.denominator, int16(_tokens.priorityToken.decimals) - PRICE_OFFSET - VOLUME_OFFSET ); // Calculate darknode fees ValueWithFees memory priorityVwF = subtractDarknodeFee(priorityTokenVolume); return SettlementDetails({ leftVolume: 0, rightVolume: 0, leftTokenFee: priorityVwF.fees, rightTokenFee: priorityVwF.fees, leftTokenAddress: _tokens.priorityToken.addr, rightTokenAddress: _tokens.priorityToken.addr }); } else { // Currently, at least one token must be Ethereum-based. // This will be implemented in the future. revert("non-eth atomic swaps are not supported"); } } /// @notice Order parity is set by the order tokens are listed. This returns /// whether an order is a buy or a sell. /// @return true if _orderID is a buy order. function isBuyOrder(bytes32 _orderID) private view returns (bool) { uint64 tokens = orderDetails[_orderID].tokens; uint32 firstToken = uint32(tokens >> 32); uint32 secondaryToken = uint32(tokens); return (firstToken < secondaryToken); } /// @return (value - fee, fee) where fee is 0.2% of value function subtractDarknodeFee(uint256 _value) private pure returns (ValueWithFees memory) { uint256 newValue = (_value * (DARKNODE_FEES_DENOMINATOR - DARKNODE_FEES_NUMERATOR)) / DARKNODE_FEES_DENOMINATOR; return ValueWithFees(newValue, _value - newValue); } /// @notice Gets the order details of the priority and secondary token from /// the RenExTokens contract and returns them as a single struct. /// /// @param _tokens The 64-bit combined token identifiers. /// @return A TokenPair struct containing two TokenDetails structs. function getTokenDetails(uint64 _tokens) private view returns (TokenPair memory) { ( address priorityAddress, uint8 priorityDecimals, bool priorityRegistered ) = renExTokensContract.tokens(uint32(_tokens >> 32)); ( address secondaryAddress, uint8 secondaryDecimals, bool secondaryRegistered ) = renExTokensContract.tokens(uint32(_tokens)); return TokenPair({ priorityToken: RenExTokens.TokenDetails(priorityAddress, priorityDecimals, priorityRegistered), secondaryToken: RenExTokens.TokenDetails(secondaryAddress, secondaryDecimals, secondaryRegistered) }); } /// @return true if _tokenAddress is 0x0, representing a token that is not /// on Ethereum function isEthereumBased(address _tokenAddress) private pure returns (bool) { return (_tokenAddress != address(0x0)); } /// @notice Computes (_numerator / _denominator) * 10 ** _scale function joinFraction(uint256 _numerator, uint256 _denominator, int16 _scale) private pure returns (uint256) { if (_scale >= 0) { // Check that (10**_scale) doesn't overflow assert(_scale <= 77); // log10(2**256) = 77.06 return _numerator.mul(10 ** uint256(_scale)) / _denominator; } else { /// @dev If _scale is less than -77, 10**-_scale would overflow. // For now, -_scale > -24 (when a token has 0 decimals and // VOLUME_OFFSET and PRICE_OFFSET are each 12). It is unlikely these // will be increased to add to more than 77. // assert((-_scale) <= 77); // log10(2**256) = 77.06 return (_numerator / _denominator) / 10 ** uint256(-_scale); } } } /// @notice RenExBrokerVerifier implements the BrokerVerifier contract, /// verifying broker signatures for order opening and fund withdrawal. contract RenExBrokerVerifier is Ownable { string public VERSION; // Passed in as a constructor parameter. // Events event LogBalancesContractUpdated(address previousBalancesContract, address nextBalancesContract); event LogBrokerRegistered(address broker); event LogBrokerDeregistered(address broker); // Storage mapping(address => bool) public brokers; mapping(address => uint256) public traderNonces; address public balancesContract; modifier onlyBalancesContract() { require(msg.sender == balancesContract, "not authorized"); _; } /// @notice The contract constructor. /// /// @param _VERSION A string defining the contract version. constructor(string _VERSION) public { VERSION = _VERSION; } /// @notice Allows the owner of the contract to update the address of the /// RenExBalances contract. /// /// @param _balancesContract The address of the new balances contract function updateBalancesContract(address _balancesContract) external onlyOwner { emit LogBalancesContractUpdated(balancesContract, _balancesContract); balancesContract = _balancesContract; } /// @notice Approved an address to sign order-opening and withdrawals. /// @param _broker The address of the broker. function registerBroker(address _broker) external onlyOwner { require(!brokers[_broker], "already registered"); brokers[_broker] = true; emit LogBrokerRegistered(_broker); } /// @notice Reverts the a broker's registration. /// @param _broker The address of the broker. function deregisterBroker(address _broker) external onlyOwner { require(brokers[_broker], "not registered"); brokers[_broker] = false; emit LogBrokerDeregistered(_broker); } /// @notice Verifies a broker's signature for an order opening. /// The data signed by the broker is a prefixed message and the order ID. /// /// @param _trader The trader requesting the withdrawal. /// @param _signature The 65-byte signature from the broker. /// @param _orderID The 32-byte order ID. /// @return True if the signature is valid, false otherwise. function verifyOpenSignature( address _trader, bytes _signature, bytes32 _orderID ) external view returns (bool) { bytes memory data = abi.encodePacked("Republic Protocol: open: ", _trader, _orderID); address signer = Utils.addr(data, _signature); return (brokers[signer] == true); } /// @notice Verifies a broker's signature for a trader withdrawal. /// The data signed by the broker is a prefixed message, the trader address /// and a 256-bit trader nonce, which is incremented every time a valid /// signature is checked. /// /// @param _trader The trader requesting the withdrawal. /// @param _signature 65-byte signature from the broker. /// @return True if the signature is valid, false otherwise. function verifyWithdrawSignature( address _trader, bytes _signature ) external onlyBalancesContract returns (bool) { bytes memory data = abi.encodePacked("Republic Protocol: withdraw: ", _trader, traderNonces[_trader]); address signer = Utils.addr(data, _signature); if (brokers[signer]) { traderNonces[_trader] += 1; return true; } return false; } } /// @notice RenExBalances is responsible for holding RenEx trader funds. contract RenExBalances is Ownable { using SafeMath for uint256; using CompatibleERC20Functions for CompatibleERC20; string public VERSION; // Passed in as a constructor parameter. RenExSettlement public settlementContract; RenExBrokerVerifier public brokerVerifierContract; DarknodeRewardVault public rewardVaultContract; /// @dev Should match the address in the DarknodeRewardVault address constant public ETHEREUM = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); // Delay between a trader calling `withdrawSignal` and being able to call // `withdraw` without a broker signature. uint256 constant public SIGNAL_DELAY = 48 hours; // Events event LogBalanceDecreased(address trader, ERC20 token, uint256 value); event LogBalanceIncreased(address trader, ERC20 token, uint256 value); event LogRenExSettlementContractUpdated(address previousRenExSettlementContract, address newRenExSettlementContract); event LogRewardVaultContractUpdated(address previousRewardVaultContract, address newRewardVaultContract); event LogBrokerVerifierContractUpdated(address previousBrokerVerifierContract, address newBrokerVerifierContract); // Storage mapping(address => mapping(address => uint256)) public traderBalances; mapping(address => mapping(address => uint256)) public traderWithdrawalSignals; /// @notice The contract constructor. /// /// @param _VERSION A string defining the contract version. /// @param _rewardVaultContract The address of the RewardVault contract. constructor( string _VERSION, DarknodeRewardVault _rewardVaultContract, RenExBrokerVerifier _brokerVerifierContract ) public { VERSION = _VERSION; rewardVaultContract = _rewardVaultContract; brokerVerifierContract = _brokerVerifierContract; } /// @notice Restricts a function to only being called by the RenExSettlement /// contract. modifier onlyRenExSettlementContract() { require(msg.sender == address(settlementContract), "not authorized"); _; } /// @notice Restricts trader withdrawing to be called if a signature from a /// RenEx broker is provided, or if a certain amount of time has passed /// since a trader has called `signalBackupWithdraw`. /// @dev If the trader is withdrawing after calling `signalBackupWithdraw`, /// this will reset the time to zero, writing to storage. modifier withBrokerSignatureOrSignal(address _token, bytes _signature) { address trader = msg.sender; // If a signature has been provided, verify it - otherwise, verify that // the user has signalled the withdraw if (_signature.length > 0) { require (brokerVerifierContract.verifyWithdrawSignature(trader, _signature), "invalid signature"); } else { require(traderWithdrawalSignals[trader][_token] != 0, "not signalled"); /* solium-disable-next-line security/no-block-members */ require((now - traderWithdrawalSignals[trader][_token]) > SIGNAL_DELAY, "signal time remaining"); traderWithdrawalSignals[trader][_token] = 0; } _; } /// @notice Allows the owner of the contract to update the address of the /// RenExSettlement contract. /// /// @param _newSettlementContract the address of the new settlement contract function updateRenExSettlementContract(RenExSettlement _newSettlementContract) external onlyOwner { emit LogRenExSettlementContractUpdated(settlementContract, _newSettlementContract); settlementContract = _newSettlementContract; } /// @notice Allows the owner of the contract to update the address of the /// DarknodeRewardVault contract. /// /// @param _newRewardVaultContract the address of the new reward vault contract function updateRewardVaultContract(DarknodeRewardVault _newRewardVaultContract) external onlyOwner { emit LogRewardVaultContractUpdated(rewardVaultContract, _newRewardVaultContract); rewardVaultContract = _newRewardVaultContract; } /// @notice Allows the owner of the contract to update the address of the /// RenExBrokerVerifier contract. /// /// @param _newBrokerVerifierContract the address of the new broker verifier contract function updateBrokerVerifierContract(RenExBrokerVerifier _newBrokerVerifierContract) external onlyOwner { emit LogBrokerVerifierContractUpdated(brokerVerifierContract, _newBrokerVerifierContract); brokerVerifierContract = _newBrokerVerifierContract; } /// @notice Transfer a token value from one trader to another, transferring /// a fee to the RewardVault. Can only be called by the RenExSettlement /// contract. /// /// @param _traderFrom The address of the trader to decrement the balance of. /// @param _traderTo The address of the trader to increment the balance of. /// @param _token The token's address. /// @param _value The number of tokens to decrement the balance by (in the /// token's smallest unit). /// @param _fee The fee amount to forward on to the RewardVault. /// @param _feePayee The recipient of the fee. function transferBalanceWithFee(address _traderFrom, address _traderTo, address _token, uint256 _value, uint256 _fee, address _feePayee) external onlyRenExSettlementContract { require(traderBalances[_traderFrom][_token] >= _fee, "insufficient funds for fee"); if (address(_token) == ETHEREUM) { rewardVaultContract.deposit.value(_fee)(_feePayee, ERC20(_token), _fee); } else { CompatibleERC20(_token).safeApprove(rewardVaultContract, _fee); rewardVaultContract.deposit(_feePayee, ERC20(_token), _fee); } privateDecrementBalance(_traderFrom, ERC20(_token), _value + _fee); if (_value > 0) { privateIncrementBalance(_traderTo, ERC20(_token), _value); } } /// @notice Deposits ETH or an ERC20 token into the contract. /// /// @param _token The token's address (must be a registered token). /// @param _value The amount to deposit in the token's smallest unit. function deposit(ERC20 _token, uint256 _value) external payable { address trader = msg.sender; uint256 receivedValue = _value; if (address(_token) == ETHEREUM) { require(msg.value == _value, "mismatched value parameter and tx value"); } else { require(msg.value == 0, "unexpected ether transfer"); receivedValue = CompatibleERC20(_token).safeTransferFromWithFees(trader, this, _value); } privateIncrementBalance(trader, _token, receivedValue); } /// @notice Withdraws ETH or an ERC20 token from the contract. A broker /// signature is required to guarantee that the trader has a sufficient /// balance after accounting for open orders. As a trustless backup, /// traders can withdraw 48 hours after calling `signalBackupWithdraw`. /// /// @param _token The token's address. /// @param _value The amount to withdraw in the token's smallest unit. /// @param _signature The broker signature function withdraw(ERC20 _token, uint256 _value, bytes _signature) external withBrokerSignatureOrSignal(_token, _signature) { address trader = msg.sender; privateDecrementBalance(trader, _token, _value); if (address(_token) == ETHEREUM) { trader.transfer(_value); } else { CompatibleERC20(_token).safeTransfer(trader, _value); } } /// @notice A trader can withdraw without needing a broker signature if they /// first call `signalBackupWithdraw` for the token they want to withdraw. /// The trader can only withdraw the particular token once for each call to /// this function. Traders can signal the intent to withdraw multiple /// tokens. /// Once this function is called, brokers will not sign order-opens for the /// trader until the trader has withdrawn, guaranteeing that they won't have /// orders open for the particular token. function signalBackupWithdraw(address _token) external { /* solium-disable-next-line security/no-block-members */ traderWithdrawalSignals[msg.sender][_token] = now; } function privateIncrementBalance(address _trader, ERC20 _token, uint256 _value) private { traderBalances[_trader][_token] = traderBalances[_trader][_token].add(_value); emit LogBalanceIncreased(_trader, _token, _value); } function privateDecrementBalance(address _trader, ERC20 _token, uint256 _value) private { require(traderBalances[_trader][_token] >= _value, "insufficient funds"); traderBalances[_trader][_token] = traderBalances[_trader][_token].sub(_value); emit LogBalanceDecreased(_trader, _token, _value); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"constant":true,"inputs":[],"name":"brokerVerifierContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newSettlementContract","type":"address"}],"name":"updateRenExSettlementContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rewardVaultContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_value","type":"uint256"},{"name":"_signature","type":"bytes"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_traderFrom","type":"address"},{"name":"_traderTo","type":"address"},{"name":"_token","type":"address"},{"name":"_value","type":"uint256"},{"name":"_fee","type":"uint256"},{"name":"_feePayee","type":"address"}],"name":"transferBalanceWithFee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"},{"name":"_value","type":"uint256"}],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"_newBrokerVerifierContract","type":"address"}],"name":"updateBrokerVerifierContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"}],"name":"signalBackupWithdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"traderBalances","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"SIGNAL_DELAY","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newRewardVaultContract","type":"address"}],"name":"updateRewardVaultContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"settlementContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ETHEREUM","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"traderWithdrawalSignals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"VERSION","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_VERSION","type":"string"},{"name":"_rewardVaultContract","type":"address"},{"name":"_brokerVerifierContract","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"trader","type":"address"},{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"LogBalanceDecreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"trader","type":"address"},{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"LogBalanceIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"previousRenExSettlementContract","type":"address"},{"indexed":false,"name":"newRenExSettlementContract","type":"address"}],"name":"LogRenExSettlementContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"previousRewardVaultContract","type":"address"},{"indexed":false,"name":"newRewardVaultContract","type":"address"}],"name":"LogRewardVaultContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"previousBrokerVerifierContract","type":"address"},{"indexed":false,"name":"newBrokerVerifierContract","type":"address"}],"name":"LogBrokerVerifierContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"}],"name":"OwnershipRenounced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]
Contract Creation Code
60806040523480156200001157600080fd5b50604051620016323803806200163283398101604090815281516020808401519284015160008054600160a060020a031916331790559190930180519093620000609160019186019062000094565b5060048054600160a060020a03938416600160a060020a031991821617909155600380549290931691161790555062000139565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620000d757805160ff191683800117855562000107565b8280016001018555821562000107579182015b8281111562000107578251825591602001919060010190620000ea565b506200011592915062000119565b5090565b6200013691905b8082111562000115576000815560010162000120565b90565b6114e980620001496000396000f3006080604052600436106100e25763ffffffff60e060020a600035041663080bdfa881146100e75780631d65551d1461011857806323017a3a1461013b57806331f092651461015057806334814e581461018157806347e7ef24146101bc57806367aa50ae146101d3578063715018a6146101f45780638da5cb5b1461020957806395b8cf551461021e578063c43c633b1461023f578063e12cbb3c14610278578063e1c3aedc1461028d578063ea42418b146102ae578063f2fde38b146102c3578063f7cdf47c146102e4578063fc257baa146102f9578063ffa1ad7414610320575b600080fd5b3480156100f357600080fd5b506100fc6103aa565b60408051600160a060020a039092168252519081900360200190f35b34801561012457600080fd5b50610139600160a060020a03600435166103b9565b005b34801561014757600080fd5b506100fc610447565b34801561015c57600080fd5b5061013960048035600160a060020a0316906024803591604435918201910135610456565b34801561018d57600080fd5b50610139600160a060020a03600435811690602435811690604435811690606435906084359060a435166107ab565b610139600160a060020a0360043516602435610a24565b3480156101df57600080fd5b50610139600160a060020a0360043516610b52565b34801561020057600080fd5b50610139610be0565b34801561021557600080fd5b506100fc610c4c565b34801561022a57600080fd5b50610139600160a060020a0360043516610c5b565b34801561024b57600080fd5b50610266600160a060020a0360043581169060243516610c84565b60408051918252519081900360200190f35b34801561028457600080fd5b50610266610ca1565b34801561029957600080fd5b50610139600160a060020a0360043516610ca8565b3480156102ba57600080fd5b506100fc610d36565b3480156102cf57600080fd5b50610139600160a060020a0360043516610d45565b3480156102f057600080fd5b506100fc610d68565b34801561030557600080fd5b50610266600160a060020a0360043581169060243516610d80565b34801561032c57600080fd5b50610335610d9d565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561036f578181015183820152602001610357565b50505050905090810190601f16801561039c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600354600160a060020a031681565b600054600160a060020a031633146103d057600080fd5b60025460408051600160a060020a039283168152918316602083015280517f8108e388ca125ce52d732c3507bc30e14194e147d133753ca55d6b5f109467ae9281900390910190a16002805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600454600160a060020a031681565b60008483838080601f016020809104026020016040519081016040528093929190818152602001838380828437505084513394506000101592506105ef91505057600354604080517fc043df8c000000000000000000000000000000000000000000000000000000008152600160a060020a038481166004830190815260248301938452865160448401528651919094169363c043df8c9386938893606490910190602085019080838360005b8381101561051b578181015183820152602001610503565b50505050905090810190601f1680156105485780820380516001836020036101000a031916815260200191505b509350505050602060405180830381600087803b15801561056857600080fd5b505af115801561057c573d6000803e3d6000fd5b505050506040513d602081101561059257600080fd5b505115156105ea576040805160e560020a62461bcd02815260206004820152601160248201527f696e76616c6964207369676e6174757265000000000000000000000000000000604482015290519081900360640190fd5b610718565b600160a060020a03808216600090815260066020908152604080832093871683529290522054151561066b576040805160e560020a62461bcd02815260206004820152600d60248201527f6e6f74207369676e616c6c656400000000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a038082166000908152600660209081526040808320938716835292905220546202a3004291909103116106ef576040805160e560020a62461bcd02815260206004820152601560248201527f7369676e616c2074696d652072656d61696e696e670000000000000000000000604482015290519081900360640190fd5b600160a060020a0380821660009081526006602090815260408083209387168352929052908120555b339350610726848989610e2a565b600160a060020a03881673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141561078757604051600160a060020a0385169088156108fc029089906000818181858888f19350505050158015610781573d6000803e3d6000fd5b506107a1565b6107a1600160a060020a038916858963ffffffff610f4d16565b5050505050505050565b600254600160a060020a0316331461080d576040805160e560020a62461bcd02815260206004820152600e60248201527f6e6f7420617574686f72697a6564000000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a0380871660009081526005602090815260408083209388168352929052205482111561088a576040805160e560020a62461bcd02815260206004820152601a60248201527f696e73756666696369656e742066756e647320666f7220666565000000000000604482015290519081900360640190fd5b600160a060020a03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156109485760048054604080517f8340f549000000000000000000000000000000000000000000000000000000008152600160a060020a038581169482019490945287841660248201526044810186905290519290911691638340f549918591606480830192600092919082900301818588803b15801561092a57600080fd5b505af115801561093e573d6000803e3d6000fd5b50505050506109fb565b60045461096890600160a060020a0386811691168463ffffffff61102b16565b60048054604080517f8340f549000000000000000000000000000000000000000000000000000000008152600160a060020a038581169482019490945287841660248201526044810186905290519290911691638340f5499160648082019260009290919082900301818387803b1580156109e257600080fd5b505af11580156109f6573d6000803e3d6000fd5b505050505b610a088685848601610e2a565b6000831115610a1c57610a1c858585611104565b505050505050565b3381600160a060020a03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415610acd57348314610ac8576040805160e560020a62461bcd02815260206004820152602760248201527f6d69736d6174636865642076616c756520706172616d6574657220616e64207460448201527f782076616c756500000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b610b41565b3415610b23576040805160e560020a62461bcd02815260206004820152601960248201527f756e6578706563746564206574686572207472616e7366657200000000000000604482015290519081900360640190fd5b610b3e600160a060020a03851683308663ffffffff6111aa16565b90505b610b4c828583611104565b50505050565b600054600160a060020a03163314610b6957600080fd5b60035460408051600160a060020a039283168152918316602083015280517f320ee0620117fe5b31d2b2ca97b2a711e2045489d6eb5290a73f04747f1819be9281900390910190a16003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600054600160a060020a03163314610bf757600080fd5b60008054604051600160a060020a03909116917ff8df31144d9c2f0f6b59d69b8b98abd5459d07f2742c4df920b25aae33c6482091a26000805473ffffffffffffffffffffffffffffffffffffffff19169055565b600054600160a060020a031681565b336000908152600660209081526040808320600160a060020a0394909416835292905220429055565b600560209081526000928352604080842090915290825290205481565b6202a30081565b600054600160a060020a03163314610cbf57600080fd5b60045460408051600160a060020a039283168152918316602083015280517f6c348498f095f3d7eb84de1c0bf7fd7db8217d2fdd2af573ad0fa3642901c2459281900390910190a16004805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600254600160a060020a031681565b600054600160a060020a03163314610d5c57600080fd5b610d65816113cf565b50565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b600660209081526000928352604080842090915290825290205481565b60018054604080516020600284861615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610e225780601f10610df757610100808354040283529160200191610e22565b820191906000526020600020905b815481529060010190602001808311610e0557829003601f168201915b505050505081565b600160a060020a03808416600090815260056020908152604080832093861683529290522054811115610ea7576040805160e560020a62461bcd02815260206004820152601260248201527f696e73756666696369656e742066756e64730000000000000000000000000000604482015290519081900360640190fd5b600160a060020a03808416600090815260056020908152604080832093861683529290522054610edd908263ffffffff61144c16565b600160a060020a038085166000818152600560209081526040808320948816808452948252918290209490945580519182529281019190915280820183905290517f2622669645a3d6b14dc5d6134367eb988c1b617ea2df59f8aa0f102ba049977c9181900360600190a1505050565b82600160a060020a031663a9059cbb83836040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b158015610fb057600080fd5b505af1158015610fc4573d6000803e3d6000fd5b50505050610fd061145e565b1515611026576040805160e560020a62461bcd02815260206004820152600f60248201527f7472616e73666572206661696c65640000000000000000000000000000000000604482015290519081900360640190fd5b505050565b82600160a060020a031663095ea7b383836040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b15801561108e57600080fd5b505af11580156110a2573d6000803e3d6000fd5b505050506110ae61145e565b1515611026576040805160e560020a62461bcd02815260206004820152600e60248201527f617070726f7665206661696c6564000000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a0380841660009081526005602090815260408083209386168352929052205461113a908263ffffffff61149216565b600160a060020a038085166000818152600560209081526040808320948816808452948252918290209490945580519182529281019190915280820183905290517f8d6dc51e8945c5e6a4ddb872612ec2b1f5e375a97daebdbb6a03a64e7c6592099181900360600190a1505050565b600080600086600160a060020a03166370a08231866040518263ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b15801561120a57600080fd5b505af115801561121e573d6000803e3d6000fd5b505050506040513d602081101561123457600080fd5b5051604080517f23b872dd000000000000000000000000000000000000000000000000000000008152600160a060020a0389811660048301528881166024830152604482018890529151929450908916916323b872dd9160648082019260009290919082900301818387803b1580156112ac57600080fd5b505af11580156112c0573d6000803e3d6000fd5b505050506112cc61145e565b1515611322576040805160e560020a62461bcd02815260206004820152601360248201527f7472616e7366657246726f6d206661696c656400000000000000000000000000604482015290519081900360640190fd5b86600160a060020a03166370a08231866040518263ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b15801561137d57600080fd5b505af1158015611391573d6000803e3d6000fd5b505050506040513d60208110156113a757600080fd5b505190506113c4846113bf838563ffffffff61144c16565b6114a5565b979650505050505050565b600160a060020a03811615156113e457600080fd5b60008054604051600160a060020a03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b60008282111561145857fe5b50900390565b6000803d8015611475576020811461147e5761148a565b6001915061148a565b60206000803e60005191505b501515919050565b8181018281101561149f57fe5b92915050565b60008183106114b457816114b6565b825b93925050505600a165627a7a72305820598ffd68e54c83a8aac3208c875bee2d7b9684c803d2575e42a61153209fcbaf00290000000000000000000000000000000000000000000000000000000000000060000000000000000000000000880407c9cd119bef48b1821cdfc434e3ca3cd58800000000000000000000000031a0d1a199631d244761eeba67e8501296d2e383000000000000000000000000000000000000000000000000000000000000000d6d61696e6e65742d312e302e3100000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436106100e25763ffffffff60e060020a600035041663080bdfa881146100e75780631d65551d1461011857806323017a3a1461013b57806331f092651461015057806334814e581461018157806347e7ef24146101bc57806367aa50ae146101d3578063715018a6146101f45780638da5cb5b1461020957806395b8cf551461021e578063c43c633b1461023f578063e12cbb3c14610278578063e1c3aedc1461028d578063ea42418b146102ae578063f2fde38b146102c3578063f7cdf47c146102e4578063fc257baa146102f9578063ffa1ad7414610320575b600080fd5b3480156100f357600080fd5b506100fc6103aa565b60408051600160a060020a039092168252519081900360200190f35b34801561012457600080fd5b50610139600160a060020a03600435166103b9565b005b34801561014757600080fd5b506100fc610447565b34801561015c57600080fd5b5061013960048035600160a060020a0316906024803591604435918201910135610456565b34801561018d57600080fd5b50610139600160a060020a03600435811690602435811690604435811690606435906084359060a435166107ab565b610139600160a060020a0360043516602435610a24565b3480156101df57600080fd5b50610139600160a060020a0360043516610b52565b34801561020057600080fd5b50610139610be0565b34801561021557600080fd5b506100fc610c4c565b34801561022a57600080fd5b50610139600160a060020a0360043516610c5b565b34801561024b57600080fd5b50610266600160a060020a0360043581169060243516610c84565b60408051918252519081900360200190f35b34801561028457600080fd5b50610266610ca1565b34801561029957600080fd5b50610139600160a060020a0360043516610ca8565b3480156102ba57600080fd5b506100fc610d36565b3480156102cf57600080fd5b50610139600160a060020a0360043516610d45565b3480156102f057600080fd5b506100fc610d68565b34801561030557600080fd5b50610266600160a060020a0360043581169060243516610d80565b34801561032c57600080fd5b50610335610d9d565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561036f578181015183820152602001610357565b50505050905090810190601f16801561039c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600354600160a060020a031681565b600054600160a060020a031633146103d057600080fd5b60025460408051600160a060020a039283168152918316602083015280517f8108e388ca125ce52d732c3507bc30e14194e147d133753ca55d6b5f109467ae9281900390910190a16002805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600454600160a060020a031681565b60008483838080601f016020809104026020016040519081016040528093929190818152602001838380828437505084513394506000101592506105ef91505057600354604080517fc043df8c000000000000000000000000000000000000000000000000000000008152600160a060020a038481166004830190815260248301938452865160448401528651919094169363c043df8c9386938893606490910190602085019080838360005b8381101561051b578181015183820152602001610503565b50505050905090810190601f1680156105485780820380516001836020036101000a031916815260200191505b509350505050602060405180830381600087803b15801561056857600080fd5b505af115801561057c573d6000803e3d6000fd5b505050506040513d602081101561059257600080fd5b505115156105ea576040805160e560020a62461bcd02815260206004820152601160248201527f696e76616c6964207369676e6174757265000000000000000000000000000000604482015290519081900360640190fd5b610718565b600160a060020a03808216600090815260066020908152604080832093871683529290522054151561066b576040805160e560020a62461bcd02815260206004820152600d60248201527f6e6f74207369676e616c6c656400000000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a038082166000908152600660209081526040808320938716835292905220546202a3004291909103116106ef576040805160e560020a62461bcd02815260206004820152601560248201527f7369676e616c2074696d652072656d61696e696e670000000000000000000000604482015290519081900360640190fd5b600160a060020a0380821660009081526006602090815260408083209387168352929052908120555b339350610726848989610e2a565b600160a060020a03881673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee141561078757604051600160a060020a0385169088156108fc029089906000818181858888f19350505050158015610781573d6000803e3d6000fd5b506107a1565b6107a1600160a060020a038916858963ffffffff610f4d16565b5050505050505050565b600254600160a060020a0316331461080d576040805160e560020a62461bcd02815260206004820152600e60248201527f6e6f7420617574686f72697a6564000000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a0380871660009081526005602090815260408083209388168352929052205482111561088a576040805160e560020a62461bcd02815260206004820152601a60248201527f696e73756666696369656e742066756e647320666f7220666565000000000000604482015290519081900360640190fd5b600160a060020a03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14156109485760048054604080517f8340f549000000000000000000000000000000000000000000000000000000008152600160a060020a038581169482019490945287841660248201526044810186905290519290911691638340f549918591606480830192600092919082900301818588803b15801561092a57600080fd5b505af115801561093e573d6000803e3d6000fd5b50505050506109fb565b60045461096890600160a060020a0386811691168463ffffffff61102b16565b60048054604080517f8340f549000000000000000000000000000000000000000000000000000000008152600160a060020a038581169482019490945287841660248201526044810186905290519290911691638340f5499160648082019260009290919082900301818387803b1580156109e257600080fd5b505af11580156109f6573d6000803e3d6000fd5b505050505b610a088685848601610e2a565b6000831115610a1c57610a1c858585611104565b505050505050565b3381600160a060020a03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1415610acd57348314610ac8576040805160e560020a62461bcd02815260206004820152602760248201527f6d69736d6174636865642076616c756520706172616d6574657220616e64207460448201527f782076616c756500000000000000000000000000000000000000000000000000606482015290519081900360840190fd5b610b41565b3415610b23576040805160e560020a62461bcd02815260206004820152601960248201527f756e6578706563746564206574686572207472616e7366657200000000000000604482015290519081900360640190fd5b610b3e600160a060020a03851683308663ffffffff6111aa16565b90505b610b4c828583611104565b50505050565b600054600160a060020a03163314610b6957600080fd5b60035460408051600160a060020a039283168152918316602083015280517f320ee0620117fe5b31d2b2ca97b2a711e2045489d6eb5290a73f04747f1819be9281900390910190a16003805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600054600160a060020a03163314610bf757600080fd5b60008054604051600160a060020a03909116917ff8df31144d9c2f0f6b59d69b8b98abd5459d07f2742c4df920b25aae33c6482091a26000805473ffffffffffffffffffffffffffffffffffffffff19169055565b600054600160a060020a031681565b336000908152600660209081526040808320600160a060020a0394909416835292905220429055565b600560209081526000928352604080842090915290825290205481565b6202a30081565b600054600160a060020a03163314610cbf57600080fd5b60045460408051600160a060020a039283168152918316602083015280517f6c348498f095f3d7eb84de1c0bf7fd7db8217d2fdd2af573ad0fa3642901c2459281900390910190a16004805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b600254600160a060020a031681565b600054600160a060020a03163314610d5c57600080fd5b610d65816113cf565b50565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b600660209081526000928352604080842090915290825290205481565b60018054604080516020600284861615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610e225780601f10610df757610100808354040283529160200191610e22565b820191906000526020600020905b815481529060010190602001808311610e0557829003601f168201915b505050505081565b600160a060020a03808416600090815260056020908152604080832093861683529290522054811115610ea7576040805160e560020a62461bcd02815260206004820152601260248201527f696e73756666696369656e742066756e64730000000000000000000000000000604482015290519081900360640190fd5b600160a060020a03808416600090815260056020908152604080832093861683529290522054610edd908263ffffffff61144c16565b600160a060020a038085166000818152600560209081526040808320948816808452948252918290209490945580519182529281019190915280820183905290517f2622669645a3d6b14dc5d6134367eb988c1b617ea2df59f8aa0f102ba049977c9181900360600190a1505050565b82600160a060020a031663a9059cbb83836040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b158015610fb057600080fd5b505af1158015610fc4573d6000803e3d6000fd5b50505050610fd061145e565b1515611026576040805160e560020a62461bcd02815260206004820152600f60248201527f7472616e73666572206661696c65640000000000000000000000000000000000604482015290519081900360640190fd5b505050565b82600160a060020a031663095ea7b383836040518363ffffffff1660e060020a0281526004018083600160a060020a0316600160a060020a0316815260200182815260200192505050600060405180830381600087803b15801561108e57600080fd5b505af11580156110a2573d6000803e3d6000fd5b505050506110ae61145e565b1515611026576040805160e560020a62461bcd02815260206004820152600e60248201527f617070726f7665206661696c6564000000000000000000000000000000000000604482015290519081900360640190fd5b600160a060020a0380841660009081526005602090815260408083209386168352929052205461113a908263ffffffff61149216565b600160a060020a038085166000818152600560209081526040808320948816808452948252918290209490945580519182529281019190915280820183905290517f8d6dc51e8945c5e6a4ddb872612ec2b1f5e375a97daebdbb6a03a64e7c6592099181900360600190a1505050565b600080600086600160a060020a03166370a08231866040518263ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b15801561120a57600080fd5b505af115801561121e573d6000803e3d6000fd5b505050506040513d602081101561123457600080fd5b5051604080517f23b872dd000000000000000000000000000000000000000000000000000000008152600160a060020a0389811660048301528881166024830152604482018890529151929450908916916323b872dd9160648082019260009290919082900301818387803b1580156112ac57600080fd5b505af11580156112c0573d6000803e3d6000fd5b505050506112cc61145e565b1515611322576040805160e560020a62461bcd02815260206004820152601360248201527f7472616e7366657246726f6d206661696c656400000000000000000000000000604482015290519081900360640190fd5b86600160a060020a03166370a08231866040518263ffffffff1660e060020a0281526004018082600160a060020a0316600160a060020a03168152602001915050602060405180830381600087803b15801561137d57600080fd5b505af1158015611391573d6000803e3d6000fd5b505050506040513d60208110156113a757600080fd5b505190506113c4846113bf838563ffffffff61144c16565b6114a5565b979650505050505050565b600160a060020a03811615156113e457600080fd5b60008054604051600160a060020a03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a36000805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b60008282111561145857fe5b50900390565b6000803d8015611475576020811461147e5761148a565b6001915061148a565b60206000803e60005191505b501515919050565b8181018281101561149f57fe5b92915050565b60008183106114b457816114b6565b825b93925050505600a165627a7a72305820598ffd68e54c83a8aac3208c875bee2d7b9684c803d2575e42a61153209fcbaf0029
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000880407c9cd119bef48b1821cdfc434e3ca3cd58800000000000000000000000031a0d1a199631d244761eeba67e8501296d2e383000000000000000000000000000000000000000000000000000000000000000d6d61696e6e65742d312e302e3100000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _VERSION (string): mainnet-1.0.1
Arg [1] : _rewardVaultContract (address): 0x880407C9Cd119BeF48b1821CdfC434e3ca3cd588
Arg [2] : _brokerVerifierContract (address): 0x31a0D1A199631D244761EEba67e8501296d2E383
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [1] : 000000000000000000000000880407c9cd119bef48b1821cdfc434e3ca3cd588
Arg [2] : 00000000000000000000000031a0d1a199631d244761eeba67e8501296d2e383
Arg [3] : 000000000000000000000000000000000000000000000000000000000000000d
Arg [4] : 6d61696e6e65742d312e302e3100000000000000000000000000000000000000
Swarm Source
bzzr://598ffd68e54c83a8aac3208c875bee2d7b9684c803d2575e42a61153209fcbaf
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.