Contract Address Details

0x4E8d3ad28E7F75F083Bffd645282B4e2a7a307cE


Implementation: 0xebdb62c6af21de92aac482343877d110ca117fb3
EternalStorageProxy Last Balance Update: Block #2361617
Created by 0x56d698–5c1685 at 0xa16808–834a20

Balance

12,937,332.2317484886541202 MO

Fetching tokens...

Contract name:
EternalStorageProxy




Optimization enabled
true
Compiler version
v0.4.26+commit.4563c3fc




Optimization runs
200
EVM Version
default




Verified at
2024-03-06 06:10:59.064680Z

Constructor Arguments

0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ebdb62c6af21de92aac482343877d110ca117fb3

Arg [0] (address) : 0x0000000000000000000000000000000000000000
Arg [1] (address) : 0xebdb62c6af21de92aac482343877d110ca117fb3

              

Contract source code

// File: contracts/interfaces/IBlockReward.sol
pragma solidity ^0.4.24;
interface IBlockReward {
function onCycleEnd() external;
}
// File: contracts/interfaces/IVoting.sol
pragma solidity ^0.4.24;
interface IVoting {
function onCycleEnd(address[] validators) external;
}
// File: contracts/abstracts/ValidatorSet.sol
pragma solidity ^0.4.24;
/**
* @title Interface to be implemented by consensus contract
* @author LiorRabin
* @dev abstract contract
*/
contract ValidatorSet {
/// Issue this log event to signal a desired change in validator set.
/// This will not lead to a change in active validator set until finalizeChange is called.
///
/// Only the last log event of any block can take effect.
/// If a signal is issued while another is being finalized it may never take effect.
///
/// parentHash here should be the parent block hash, or the signal will not be recognized.
event InitiateChange(bytes32 indexed parentHash, address[] newSet);
/// Get current validator set (last enacted or initial if no changes ever made)
function getValidators() external view returns(address[]);
/// Called when an initiated change reaches finality and is activated.
/// Only valid when msg.sender == SYSTEM_ADDRESS (EIP96, 2**160 - 2)
///
/// Also called when the contract is first enabled for consensus.
/// In this case, the "change" finalized is the activation of the initial set.
function finalizeChange() external;
}
// File: contracts/eternal-storage/EternalStorage.sol
pragma solidity ^0.4.24;
/**
* @title EternalStorage
* @author LiorRabin
* @dev This contract holds all the necessary state variables to carry out the storage of any contract and to support the upgrade functionality.
*/
contract EternalStorage {
// Version number of the current implementation
uint256 internal version;
// Address of the current implementation
address internal implementation;
// Storage mappings
mapping(bytes32 => uint256) internal uintStorage;
mapping(bytes32 => string) internal stringStorage;
mapping(bytes32 => address) internal addressStorage;
mapping(bytes32 => bytes) internal bytesStorage;
mapping(bytes32 => bool) internal boolStorage;
mapping(bytes32 => int256) internal intStorage;
mapping(bytes32 => uint256[]) internal uintArrayStorage;
mapping(bytes32 => string[]) internal stringArrayStorage;
mapping(bytes32 => address[]) internal addressArrayStorage;
mapping(bytes32 => bytes[]) internal bytesArrayStorage;
mapping(bytes32 => bool[]) internal boolArrayStorage;
mapping(bytes32 => int256[]) internal intArrayStorage;
mapping(bytes32 => bytes32[]) internal bytes32ArrayStorage;
function isInitialized() public view returns(bool) {
return boolStorage[keccak256(abi.encodePacked("isInitialized"))];
}
function setInitialized(bool _status) internal {
boolStorage[keccak256(abi.encodePacked("isInitialized"))] = _status;
}
}
// File: contracts/eternal-storage/EternalStorageProxy.sol
pragma solidity ^0.4.24;
/**
* @title EternalStorageProxy
* @author LiorRabin
* @dev This proxy holds the storage of the token contract and delegates every call to the current implementation set.
* Besides, it allows to upgrade the token's behaviour towards further implementations, and provides authorization control functionalities
*/
contract EternalStorageProxy is EternalStorage {
/**
* @dev This event will be emitted every time the implementation gets upgraded
* @param version representing the version number of the upgraded implementation
* @param implementation representing the address of the upgraded implementation
*/
event Upgraded(uint256 version, address indexed implementation);
/**
* @dev This event will be emitted when ownership is renounces
* @param previousOwner address which is renounced from ownership
*/
event OwnershipRenounced(address indexed previousOwner);
/**
* @dev This event will be emitted when ownership is transferred
* @param previousOwner address which represents the previous owner
* @param newOwner address which represents the new owner
*/
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev This modifier verifies that msg.sender is the ProxyStorage contract
*/
modifier onlyProxyStorage() {
require(msg.sender == getProxyStorage());
_;
}
/**
* @dev This modifier verifies that msg.sender is the owner of the contract
*/
modifier onlyOwner() {
require(msg.sender == getOwner());
_;
}
/**
* @dev Constructor
* @param _proxyStorage address representing the ProxyStorage contract
* @param _implementation address representing the implementation contract
*/
constructor(address _proxyStorage, address _implementation) public {
require(_implementation != address(0));
if (_proxyStorage != address(0)) {
_setProxyStorage(_proxyStorage);
} else {
_setProxyStorage(address(this));
}
_setImplementation(_implementation);
_setOwner(msg.sender);
}
/**
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
* This function will return whatever the implementation call returns
*/
// solhint-disable no-complex-fallback, no-inline-assembly
function() payable public {
address _impl = getImplementation();
require(_impl != address(0));
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0
calldatacopy(0, 0, calldatasize)
// Call the implementation.
// out and outsize are 0 because we don't know the size yet
let result := delegatecall(gas, _impl, 0, calldatasize, 0, 0)
// Copy the returned data
returndatacopy(0, 0, returndatasize)
switch result
// delegatecall returns 0 on error
case 0 { revert(0, returndatasize) }
default { return(0, returndatasize) }
}
}
// solhint-enable no-complex-fallback, no-inline-assembly
/**
* @dev Allows ProxyStorage contract (only) to upgrade the current implementation.
* @param _newImplementation representing the address of the new implementation to be set.
*/
function upgradeTo(address _newImplementation) public onlyProxyStorage returns(bool) {
if (_newImplementation == address(0)) return false;
if (getImplementation() == _newImplementation) return false;
uint256 _newVersion = getVersion() + 1;
_setVersion(_newVersion);
_setImplementation(_newImplementation);
emit Upgraded(_newVersion, _newImplementation);
return true;
}
/**
* @dev Allows the current owner to relinquish ownership.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipRenounced(getOwner());
_setOwner(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 {
require(_newOwner != address(0));
emit OwnershipTransferred(getOwner(), _newOwner);
_setOwner(_newOwner);
}
function getOwner() public view returns(address) {
return addressStorage[keccak256(abi.encodePacked("owner"))];
}
function _setOwner(address _owner) private {
addressStorage[keccak256(abi.encodePacked("owner"))] = _owner;
}
function getVersion() public view returns(uint256) {
return version;
}
function _setVersion(uint256 _newVersion) private {
version = _newVersion;
}
function getImplementation() public view returns(address) {
return implementation;
}
function _setImplementation(address _newImplementation) private {
implementation = _newImplementation;
}
function getProxyStorage() public view returns(address) {
return addressStorage[keccak256(abi.encodePacked("proxyStorage"))];
}
function _setProxyStorage(address _proxyStorage) private {
addressStorage[keccak256(abi.encodePacked("proxyStorage"))] = _proxyStorage;
}
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.4.24;
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library SafeMath {
int256 constant private INT256_MIN = -2**255;
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Multiplies two signed integers, reverts on overflow.
*/
function mul(int256 a, int256 b) internal pure returns (int256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
require(!(a == -1 && b == INT256_MIN)); // This is the only case of overflow not detected by the check below
int256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Integer division of two signed integers truncating the quotient, reverts on division by zero.
*/
function div(int256 a, int256 b) internal pure returns (int256) {
require(b != 0); // Solidity only automatically asserts when dividing by 0
require(!(b == -1 && a == INT256_MIN)); // This is the only case of overflow
int256 c = a / b;
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Subtracts two signed integers, reverts on overflow.
*/
function sub(int256 a, int256 b) internal pure returns (int256) {
int256 c = a - b;
require((b >= 0 && c <= a) || (b < 0 && c > a));
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Adds two signed integers, reverts on overflow.
*/
function add(int256 a, int256 b) internal pure returns (int256) {
int256 c = a + b;
require((b >= 0 && c >= a) || (b < 0 && c < a));
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
// File: contracts/ProxyStorage.sol
pragma solidity ^0.4.24;
/**
* @title Contract used for access and upgradeability to all network contracts
* @author LiorRabin
*/
contract ProxyStorage is EternalStorage {
using SafeMath for uint256;
/**
* @dev Available contract types on the network
*/
enum ContractTypes {
Invalid,
Consensus,
BlockReward,
ProxyStorage,
Voting
}
/**
* @dev This event will be emitted when all contract addresses have been initialized by the contract owner
*/
event ProxyInitialized(
address consensus,
address blockReward,
address voting
);
/**
* @dev This event will be emitted each time a contract address is updated
* @param contractType contract type (See ContractTypes enum)
* @param contractAddress contract address set for the contract type
*/
event AddressSet(uint256 contractType, address contractAddress);
/**
* @dev This modifier verifies that msg.sender is the owner of the contract
*/
modifier onlyOwner() {
require(msg.sender == addressStorage[OWNER]);
_;
}
/**
* @dev This modifier verifies that msg.sender is the voting contract which implement proxy address change
*/
modifier onlyVoting() {
require(msg.sender == getVoting());
_;
}
/**
* @dev Function to be called on contract initialization
* @param _consensus address of the network consensus contract
*/
function initialize(address _consensus) external onlyOwner {
require(!isInitialized());
require(_consensus != address(0));
require(_consensus != address(this));
_setConsensus(_consensus);
setInitialized(true);
}
/**
* @dev Function to be called to initialize all available contract types addresses
*/
function initializeAddresses(address _blockReward, address _voting) external onlyOwner {
require(!boolStorage[PROXY_STORAGE_ADDRESSES_INITIALIZED]);
addressStorage[BLOCK_REWARD] = _blockReward;
addressStorage[VOTING] = _voting;
boolStorage[PROXY_STORAGE_ADDRESSES_INITIALIZED] = true;
emit ProxyInitialized(
getConsensus(),
_blockReward,
_voting
);
}
/**
* @dev Function to be called to set specific contract type address
* @param _contractType contract type (See ContractTypes enum)
* @param _contractAddress contract address set for the contract type
*/
function setContractAddress(uint256 _contractType, address _contractAddress) external onlyVoting returns(bool) {
if (!isInitialized()) return false;
if (_contractAddress == address(0)) return false;
bool success = false;
if (_contractType == uint256(ContractTypes.Consensus)) {
success = EternalStorageProxy(getConsensus()).upgradeTo(_contractAddress);
} else if (_contractType == uint256(ContractTypes.BlockReward)) {
success = EternalStorageProxy(getBlockReward()).upgradeTo(_contractAddress);
} else if (_contractType == uint256(ContractTypes.ProxyStorage)) {
success = EternalStorageProxy(this).upgradeTo(_contractAddress);
} else if (_contractType == uint256(ContractTypes.Voting)) {
success = EternalStorageProxy(getVoting()).upgradeTo(_contractAddress);
}
if (success) {
emit AddressSet(_contractType, _contractAddress);
}
return success;
}
/**
* @dev Function checking if a contract type is valid one for proxy usage
* @param _contractType contract type to check if valid
*/
function isValidContractType(uint256 _contractType) external pure returns(bool) {
return
_contractType == uint256(ContractTypes.Consensus) ||
_contractType == uint256(ContractTypes.BlockReward) ||
_contractType == uint256(ContractTypes.ProxyStorage) ||
_contractType == uint256(ContractTypes.Voting);
}
bytes32 internal constant OWNER = keccak256(abi.encodePacked("owner"));
bytes32 internal constant CONSENSUS = keccak256(abi.encodePacked("consensus"));
bytes32 internal constant BLOCK_REWARD = keccak256(abi.encodePacked("blockReward"));
bytes32 internal constant VOTING = keccak256(abi.encodePacked("voting"));
bytes32 internal constant PROXY_STORAGE_ADDRESSES_INITIALIZED = keccak256(abi.encodePacked("proxyStorageAddressesInitialized"));
function _setConsensus(address _consensus) private {
addressStorage[CONSENSUS] = _consensus;
}
function getConsensus() public view returns(address){
return addressStorage[CONSENSUS];
}
function getBlockReward() public view returns(address){
return addressStorage[BLOCK_REWARD];
}
function getVoting() public view returns(address){
return addressStorage[VOTING];
}
}
// File: openzeppelin-solidity/contracts/math/Math.sol
pragma solidity ^0.4.24;
/**
* @title Math
* @dev Assorted math operations
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Calculates the average of two numbers. Since these are integers,
* averages of an even and odd number cannot be represented, and will be
* rounded down.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}
// File: contracts/ConsensusUtils.sol
pragma solidity ^0.4.24;
/**
* @title Consensus utility contract
* @author LiorRabin
*/
contract ConsensusUtils is EternalStorage, ValidatorSet {
using SafeMath for uint256;
uint256 public constant DECIMALS = 10 ** 18;
uint256 public constant MAX_VALIDATORS = 100;
uint256 public constant MIN_STAKE = 5e22; // 50,000
uint256 public constant MAX_STAKE = 1e24; // 1,000,000
uint256 public constant CYCLE_DURATION_BLOCKS = 28800; // 48 hours [48*60*60/6]
uint256 public constant SNAPSHOTS_PER_CYCLE = 0; // snapshot each 288 minutes [34560/10/60*5]
uint256 public constant DEFAULT_VALIDATOR_FEE = 1e17; // 10%
uint256 public constant VALIDATOR_PRODUCTIVITY_BP = 3000; // 30%
uint256 public constant MAX_STRIKE_COUNT = 5;
uint256 public constant STRIKE_RESET = 50; // reset strikes after 50 clean cycles
/**
* @dev This event will be emitted after a change to the validator set has been finalized
* @param newSet array of addresses which represent the new validator set
*/
event ChangeFinalized(address[] newSet);
/**
* @dev This event will be emitted on cycle end to indicate the `emitInitiateChange` function needs to be called to apply a new validator set
*/
event ShouldEmitInitiateChange();
/**
* @dev This modifier verifies that the change initiated has not been finalized yet
*/
modifier notFinalized() {
require(!isFinalized());
_;
}
/**
* @dev This modifier verifies that msg.sender is the system address (EIP96)
*/
modifier onlySystem() {
require(msg.sender == addressStorage[SYSTEM_ADDRESS]);
_;
}
/**
* @dev This modifier verifies that msg.sender is the owner of the contract
*/
modifier onlyOwner() {
require(msg.sender == addressStorage[OWNER]);
_;
}
/**
* @dev This modifier verifies that msg.sender is the block reward contract
*/
modifier onlyBlockReward() {
require(msg.sender == ProxyStorage(getProxyStorage()).getBlockReward());
_;
}
/**
* @dev This modifier verifies that msg.sender is a validator
*/
modifier onlyValidator() {
require(isValidator(msg.sender));
_;
}
/**
* @dev This modifier verifies that msg.sender is currently jailed
*/
modifier onlyJailedValidator() {
require(isJailed(msg.sender));
_;
}
bytes32 internal constant OWNER = keccak256(abi.encodePacked("owner"));
bytes32 internal constant SYSTEM_ADDRESS = keccak256(abi.encodePacked("SYSTEM_ADDRESS"));
bytes32 internal constant IS_FINALIZED = keccak256(abi.encodePacked("isFinalized"));
bytes32 internal constant CURRENT_CYCLE_START_BLOCK = keccak256(abi.encodePacked("currentCycleStartBlock"));
bytes32 internal constant CURRENT_CYCLE_END_BLOCK = keccak256(abi.encodePacked("currentCycleEndBlock"));
bytes32 internal constant LAST_SNAPSHOT_TAKEN_AT_BLOCK = keccak256(abi.encodePacked("lastSnapshotTakenAtBlock"));
bytes32 internal constant NEXT_SNAPSHOT_ID = keccak256(abi.encodePacked("nextSnapshotId"));
bytes32 internal constant CURRENT_VALIDATORS = keccak256(abi.encodePacked("currentValidators"));
bytes32 internal constant PENDING_VALIDATORS = keccak256(abi.encodePacked("pendingValidators"));
bytes32 internal constant PROXY_STORAGE = keccak256(abi.encodePacked("proxyStorage"));
bytes32 internal constant WAS_PROXY_STORAGE_SET = keccak256(abi.encodePacked("wasProxyStorageSet"));
bytes32 internal constant NEW_VALIDATOR_SET = keccak256(abi.encodePacked("newValidatorSet"));
bytes32 internal constant SHOULD_EMIT_INITIATE_CHANGE = keccak256(abi.encodePacked("shouldEmitInitiateChange"));
bytes32 internal constant TOTAL_STAKE_AMOUNT = keccak256(abi.encodePacked("totalStakeAmount"));
bytes32 internal constant JAILED_VALIDATORS = keccak256(abi.encodePacked("jailedValidators"));
function _delegate(address _staker, uint256 _amount, address _validator) internal {
require(_staker != address(0));
require(_amount != 0);
require(_validator != address(0));
_delegatedAmountAdd(_staker, _validator, _amount);
_stakeAmountAdd(_validator, _amount);
// stake amount of the validator isn't greater than the max stake
require(stakeAmount(_validator) <= getMaxStake());
// the validator must stake himselft the minimum stake
if (stakeAmount(_validator) >= getMinStake() && !isPendingValidator(_validator)) {
_pendingValidatorsAdd(_validator);
_setValidatorFee(_validator, DEFAULT_VALIDATOR_FEE);
}
// if _validator is one of the current validators
if (isValidator(_validator)) {
// the total stake needs to be adjusted for the block reward formula
_totalStakeAmountAdd(_amount);
}
}
function _withdraw(address _staker, uint256 _amount, address _validator) internal {
require(_validator != address(0));
require(_amount > 0);
require(_amount <= stakeAmount(_validator));
require(_amount <= delegatedAmount(_staker, _validator));
bool _isValidator = isValidator(_validator);
// if new stake amount is lesser than minStake and the validator is one of the current validators
if (stakeAmount(_validator).sub(_amount) < getMinStake() && _isValidator) {
// do not withdaw the amount until the validator is in current set
_pendingValidatorsRemove(_validator);
return;
}
_delegatedAmountSub(_staker, _validator, _amount);
_stakeAmountSub(_validator, _amount);
// if _validator is one of the current validators
if (_isValidator) {
// the total stake needs to be adjusted for the block reward formula
_totalStakeAmountSub(_amount);
}
// if validator is needed to be removed from pending, but not current
if (stakeAmount(_validator) < getMinStake()) {
_pendingValidatorsRemove(_validator);
}
_staker.transfer(_amount);
}
function _setSystemAddress(address _newAddress) internal {
addressStorage[SYSTEM_ADDRESS] = _newAddress;
}
function setProxyStorage(address _newAddress) external onlyOwner {
require(_newAddress != address(0));
require(!boolStorage[WAS_PROXY_STORAGE_SET]);
addressStorage[PROXY_STORAGE] = _newAddress;
boolStorage[WAS_PROXY_STORAGE_SET] = true;
}
function getProxyStorage() public view returns(address) {
return addressStorage[PROXY_STORAGE];
}
function _setFinalized(bool _status) internal {
boolStorage[IS_FINALIZED] = _status;
}
function isFinalized() public view returns(bool) {
return boolStorage[IS_FINALIZED];
}
/**
* returns maximum possible validators number
*/
function getMaxValidators() public pure returns(uint256) {
return MAX_VALIDATORS;
}
/**
* returns minimum stake (wei) needed to become a validator
*/
function getMinStake() public pure returns(uint256) {
return MIN_STAKE;
}
/**
* returns maximum stake (wei) for a validator
*/
function getMaxStake() public pure returns(uint256) {
return MAX_STAKE;
}
/**
* @dev Function returns the minimum validator fee amount in wei
While 100% is 1e18
*/
function getMinValidatorFee() public pure returns(uint256) {
return DEFAULT_VALIDATOR_FEE;
}
/**
* returns number of blocks per cycle (block time is 5 seconds)
*/
function getCycleDurationBlocks() public pure returns(uint256) {
return CYCLE_DURATION_BLOCKS;
}
function _setCurrentCycle() internal {
uintStorage[CURRENT_CYCLE_START_BLOCK] = block.number;
uintStorage[CURRENT_CYCLE_END_BLOCK] = block.number + getCycleDurationBlocks();
}
function _checkJail(address[] _validatorSet) internal {
uint256 expectedNumberOfBlocks = getCycleDurationBlocks().mul(VALIDATOR_PRODUCTIVITY_BP).div(_validatorSet.length).div(10000);
for (uint i = 0; i < _validatorSet.length; i++) {
if(blockCounter(_validatorSet[i]) < expectedNumberOfBlocks) {
// Validator hasn't met the desired uptime jail them and remove them from the next cycle
_jailValidator(_validatorSet[i]);
} else if (getStrikes(_validatorSet[i]) != 0) {
// Validator has met desired uptime and has strikes, inc the strike reset
_incStrikeReset(_validatorSet[i]);
}
//reset the block counter
_resetBlockCounter(_validatorSet[i]);
}
}
function _removeFromJail(address _validator) internal {
_jailedValidatorRemove(_validator);
if (stakeAmount(_validator) >= getMinStake() && !isPendingValidator(_validator)) {
_pendingValidatorsAdd(_validator);
}
}
function getCurrentCycleStartBlock() external view returns(uint256) {
return uintStorage[CURRENT_CYCLE_START_BLOCK];
}
function getCurrentCycleEndBlock() public view returns(uint256) {
return uintStorage[CURRENT_CYCLE_END_BLOCK];
}
function getReleaseBlock(address _validator) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("releaseBlock", _validator))];
}
/**
* returns number of pending validator snapshots to be saved each cycle
*/
function getSnapshotsPerCycle() public pure returns(uint256) {
return SNAPSHOTS_PER_CYCLE;
}
function _setLastSnapshotTakenAtBlock(uint256 _block) internal {
uintStorage[LAST_SNAPSHOT_TAKEN_AT_BLOCK] = _block;
}
function getLastSnapshotTakenAtBlock() public view returns(uint256) {
return uintStorage[LAST_SNAPSHOT_TAKEN_AT_BLOCK];
}
function _setNextSnapshotId(uint256 _id) internal {
uintStorage[NEXT_SNAPSHOT_ID] = _id;
}
function getNextSnapshotId() public view returns(uint256) {
return uintStorage[NEXT_SNAPSHOT_ID];
}
function _setSnapshot(uint256 _snapshotId, address[] _addresses) internal {
uint256 len = _addresses.length;
uint256 n = Math.min(getMaxValidators(), len);
address[] memory _result = new address[](n);
uint256 rand = _getSeed();
for (uint256 i = 0; i < n; i++) {
uint256 j = rand % len;
_result[i] = _addresses[j];
_addresses[j] = _addresses[len - 1];
delete _addresses[len - 1];
len--;
rand = uint256(keccak256(abi.encodePacked(rand)));
}
_setSnapshotAddresses(_snapshotId, _result);
}
function _setSnapshotAddresses(uint256 _snapshotId, address[] _addresses) internal {
addressArrayStorage[keccak256(abi.encodePacked("snapshot", _snapshotId, "addresses"))] = _addresses;
}
function getSnapshotAddresses(uint256 _snapshotId) public view returns(address[]) {
return addressArrayStorage[keccak256(abi.encodePacked("snapshot", _snapshotId, "addresses"))];
}
function currentValidators() public view returns(address[]) {
return addressArrayStorage[CURRENT_VALIDATORS];
}
function currentValidatorsLength() public view returns(uint256) {
return addressArrayStorage[CURRENT_VALIDATORS].length;
}
function jailedValidatorsLength() public view returns(uint256) {
return addressArrayStorage[JAILED_VALIDATORS].length;
}
function currentValidatorsAtPosition(uint256 _p) public view returns(address) {
return addressArrayStorage[CURRENT_VALIDATORS][_p];
}
function jailedValidatorsAtPosition(uint256 _p) public view returns(address) {
return addressArrayStorage[JAILED_VALIDATORS][_p];
}
function isValidator(address _address) public view returns(bool) {
for (uint256 i; i < currentValidatorsLength(); i++) {
if (_address == currentValidatorsAtPosition(i)) {
return true;
}
}
return false;
}
function isJailed(address _address) public view returns(bool) {
for (uint256 i; i < jailedValidatorsLength(); i++) {
if (_address == jailedValidatorsAtPosition(i)) {
return true;
}
}
return false;
}
function requiredSignatures() public view returns(uint256) {
return currentValidatorsLength().div(2).add(1);
}
function _currentValidatorsAdd(address _address) internal {
addressArrayStorage[CURRENT_VALIDATORS].push(_address);
}
function _setCurrentValidators(address[] _currentValidators) internal {
uint256 totalStake = 0;
for (uint i = 0; i < _currentValidators.length; i++) {
uint256 stakedAmount = stakeAmount(_currentValidators[i]);
totalStake = totalStake + stakedAmount;
// setting fee on all active validators to at least minimum fee
// needs to run only once for the existing validators
uint _validatorFee = validatorFee(_currentValidators[i]);
if (_validatorFee < getMinValidatorFee()) {
_setValidatorFee(_currentValidators[i], getMinValidatorFee());
}
}
_setTotalStakeAmount(totalStake);
addressArrayStorage[CURRENT_VALIDATORS] = _currentValidators;
}
function pendingValidators() public view returns(address[]) {
return addressArrayStorage[PENDING_VALIDATORS];
}
function pendingValidatorsLength() public view returns(uint256) {
return addressArrayStorage[PENDING_VALIDATORS].length;
}
function pendingValidatorsAtPosition(uint256 _p) public view returns(address) {
return addressArrayStorage[PENDING_VALIDATORS][_p];
}
function jailedValidators() public view returns(address[]) {
return addressArrayStorage[JAILED_VALIDATORS];
}
function isPendingValidator(address _address) public view returns(bool) {
for (uint256 i; i < pendingValidatorsLength(); i++) {
if (_address == pendingValidatorsAtPosition(i)) {
return true;
}
}
return false;
}
function _jailValidator(address _address) internal {
_pendingValidatorsRemove(_address);
_addJailedValidator(_address);
_setJailRelease(_address);
_resetStrikeReset(_address);
}
function _maintenance(address _address) internal {
_pendingValidatorsRemove(_address);
_addJailedValidator(_address);
}
function _setPendingValidatorsAtPosition(uint256 _p, address _address) internal {
addressArrayStorage[PENDING_VALIDATORS][_p] = _address;
}
function _setJailedValidatorsAtPosition(uint256 _p, address _address) internal {
addressArrayStorage[JAILED_VALIDATORS][_p] = _address;
}
function _pendingValidatorsAdd(address _address) internal {
require(isJailed(_address) == false);
addressArrayStorage[PENDING_VALIDATORS].push(_address);
}
function _addJailedValidator(address _address) internal {
addressArrayStorage[JAILED_VALIDATORS].push(_address);
}
function _jailedValidatorRemove(address _address) internal {
bool found = false;
uint256 removeIndex;
for (uint256 i; i < jailedValidatorsLength(); i++) {
if (_address == jailedValidatorsAtPosition(i)) {
removeIndex = i;
found = true;
break;
}
}
if (found) {
uint256 lastIndex = jailedValidatorsLength() - 1;
address lastValidator = jailedValidatorsAtPosition(lastIndex);
if (lastValidator != address(0)) {
_setJailedValidatorsAtPosition(removeIndex, lastValidator);
}
delete addressArrayStorage[JAILED_VALIDATORS][lastIndex];
addressArrayStorage[JAILED_VALIDATORS].length--;
// if the validator in on of the current validators
}
}
function _pendingValidatorsRemove(address _address) internal {
bool found = false;
uint256 removeIndex;
for (uint256 i; i < pendingValidatorsLength(); i++) {
if (_address == pendingValidatorsAtPosition(i)) {
removeIndex = i;
found = true;
break;
}
}
if (found) {
uint256 lastIndex = pendingValidatorsLength() - 1;
address lastValidator = pendingValidatorsAtPosition(lastIndex);
if (lastValidator != address(0)) {
_setPendingValidatorsAtPosition(removeIndex, lastValidator);
}
delete addressArrayStorage[PENDING_VALIDATORS][lastIndex];
addressArrayStorage[PENDING_VALIDATORS].length--;
// if the validator in on of the current validators
}
}
function stakeAmount(address _address) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("stakeAmount", _address))];
}
function totalStakeAmount() public view returns(uint256) {
return uintStorage[TOTAL_STAKE_AMOUNT];
}
function _stakeAmountAdd(address _address, uint256 _amount) internal {
uintStorage[keccak256(abi.encodePacked("stakeAmount", _address))] = uintStorage[keccak256(abi.encodePacked("stakeAmount", _address))].add(_amount);
}
function _stakeAmountSub(address _address, uint256 _amount) internal {
uintStorage[keccak256(abi.encodePacked("stakeAmount", _address))] = uintStorage[keccak256(abi.encodePacked("stakeAmount", _address))].sub(_amount);
}
function _setJailRelease(address _address) internal {
uint256 strike = uintStorage[keccak256(abi.encodePacked("strikeCount", _address))];
// release block scales based on strikes, strikes get reset after undergoing STRIKE_RESET jail free cycles
// subract one so they can flag to be released on start of the next cycle
uintStorage[keccak256(abi.encodePacked("releaseBlock", _address))] = (getCurrentCycleEndBlock().add(getCycleDurationBlocks().mul(strike)).sub(1));
if (strike <= MAX_STRIKE_COUNT) {
uintStorage[keccak256(abi.encodePacked("strikeCount", _address))] = strike + 1;
}
}
function _resetStrikes(address _address) internal {
uintStorage[keccak256(abi.encodePacked("strikeCount", _address))] = 0;
}
function delegatedAmount(address _address, address _validator) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("delegatedAmount", _address, _validator))];
}
function _delegatedAmountAdd(address _address, address _validator, uint256 _amount) internal {
uintStorage[keccak256(abi.encodePacked("delegatedAmount", _address, _validator))] = uintStorage[keccak256(abi.encodePacked("delegatedAmount", _address, _validator))].add(_amount);
if (_address != _validator && !isDelegator(_validator, _address)) {
_delegatorsAdd(_address, _validator);
}
}
function _delegatedAmountSub(address _address, address _validator, uint256 _amount) internal {
uintStorage[keccak256(abi.encodePacked("delegatedAmount", _address, _validator))] = uintStorage[keccak256(abi.encodePacked("delegatedAmount", _address, _validator))].sub(_amount);
if (uintStorage[keccak256(abi.encodePacked("delegatedAmount", _address, _validator))] == 0) {
_delegatorsRemove(_address, _validator);
}
}
function delegators(address _validator) public view returns(address[]) {
return addressArrayStorage[keccak256(abi.encodePacked("delegators", _validator))];
}
function delegatorsLength(address _validator) public view returns(uint256) {
return addressArrayStorage[keccak256(abi.encodePacked("delegators", _validator))].length;
}
function delegatorsAtPosition(address _validator, uint256 _p) public view returns(address) {
return addressArrayStorage[keccak256(abi.encodePacked("delegators", _validator))][_p];
}
function blockCounter(address _validator) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("blockCounter", _validator))];
}
function isDelegator(address _validator, address _address) public view returns(bool) {
for (uint256 i; i < delegatorsLength(_validator); i++) {
if (_address == delegatorsAtPosition(_validator, i)) {
return true;
}
}
return false;
}
function _setDelegatorsAtPosition(address _validator, uint256 _p, address _address) internal {
addressArrayStorage[keccak256(abi.encodePacked("delegators", _validator))][_p] = _address;
}
function _delegatorsAdd(address _address, address _validator) internal {
addressArrayStorage[keccak256(abi.encodePacked("delegators", _validator))].push(_address);
}
function _delegatorsRemove(address _address, address _validator) internal {
bool found = false;
uint256 removeIndex;
for (uint256 i; i < delegatorsLength(_validator); i++) {
if (_address == delegatorsAtPosition(_validator, i)) {
removeIndex = i;
found = true;
break;
}
}
if (found) {
uint256 lastIndex = delegatorsLength(_validator) - 1;
address lastDelegator = delegatorsAtPosition(_validator, lastIndex);
if (lastDelegator != address(0)) {
_setDelegatorsAtPosition(_validator, removeIndex, lastDelegator);
}
delete addressArrayStorage[keccak256(abi.encodePacked("delegators", _validator))][lastIndex];
addressArrayStorage[keccak256(abi.encodePacked("delegators", _validator))].length--;
}
}
function getDelegatorsForRewardDistribution(address _validator, uint256 _rewardAmount) public view returns(address[], uint256[]) {
address[] memory _delegators = delegators(_validator);
uint256[] memory _rewards = new uint256[](_delegators.length);
uint256 divider = Math.max(getMinStake(), stakeAmount(_validator));
for (uint256 i; i < _delegators.length; i++) {
uint256 _amount = delegatedAmount(delegatorsAtPosition(_validator, i), _validator);
_rewards[i] = _rewardAmount.mul(_amount).div(divider).mul(DECIMALS - validatorFee(_validator)).div(DECIMALS);
}
return (_delegators, _rewards);
}
function newValidatorSet() public view returns(address[]) {
return addressArrayStorage[NEW_VALIDATOR_SET];
}
function newValidatorSetLength() public view returns(uint256) {
return addressArrayStorage[NEW_VALIDATOR_SET].length;
}
function _setNewValidatorSet(address[] _newSet) internal {
addressArrayStorage[NEW_VALIDATOR_SET] = _newSet;
}
function _setTotalStakeAmount(uint256 _totalStake) internal {
uintStorage[TOTAL_STAKE_AMOUNT] = _totalStake;
}
function _totalStakeAmountAdd(uint256 _stakeAmount) internal {
uintStorage[TOTAL_STAKE_AMOUNT] = uintStorage[TOTAL_STAKE_AMOUNT].add(_stakeAmount);
}
function _totalStakeAmountSub(uint256 _stakeAmount) internal {
uintStorage[TOTAL_STAKE_AMOUNT] = uintStorage[TOTAL_STAKE_AMOUNT].sub(_stakeAmount);
}
function shouldEmitInitiateChange() public view returns(bool) {
return boolStorage[SHOULD_EMIT_INITIATE_CHANGE];
}
function _setShouldEmitInitiateChange(bool _status) internal {
boolStorage[SHOULD_EMIT_INITIATE_CHANGE] = _status;
}
function _hasCycleEnded() internal view returns(bool) {
return (block.number >= getCurrentCycleEndBlock());
}
function _getSeed() internal view returns(uint256) {
return uint256(keccak256(abi.encodePacked(blockhash(block.number - 1))));
}
function _getRandom(uint256 _from, uint256 _to) internal view returns(uint256) {
return _getSeed().mod(_to.sub(_from)).add(_from);
}
function validatorFee(address _validator) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("validatorFee", _validator))];
}
function _setValidatorFee(address _validator, uint256 _amount) internal {
uintStorage[keccak256(abi.encodePacked("validatorFee", _validator))] = _amount;
}
/**
* Internal function to be called from cycle() to increment the block counter for this validator.
* block counter is used to assess the validators uptime in a given cycle. It is zeroed at the start of each cycle.
*/
function _incBlockCounter(address _validator) internal {
uintStorage[keccak256(abi.encodePacked("blockCounter", _validator))] = uintStorage[keccak256(abi.encodePacked("blockCounter", _validator))] + 1;
}
/**
* Internal function to be called on cycle end to reset the block counter for a validator so we are ready for the new cycle
*/
function _resetBlockCounter(address _validator) internal {
uintStorage[keccak256(abi.encodePacked("blockCounter", _validator))] = 0;
}
/**
* Internal function to be called each time a validator has had a clean cycle. the strike reset counter is used to reset a validator strike count
* if it exceeds the reset threshold
*/
function _incStrikeReset(address _validator) internal {
uintStorage[keccak256(abi.encodePacked("strikeReset", _validator))] = uintStorage[keccak256(abi.encodePacked("strikeReset", _validator))] + 1;
if (uintStorage[keccak256(abi.encodePacked("strikeReset", _validator))] > STRIKE_RESET)
{
// Strike count exceeds the reset criteria, reset the strike and reset counters back to zero.
_resetStrikeReset(_validator);
_resetStrikes(_validator);
}
}
/**
* Internal function to be called after a validator has had STRIKE_RESET clean cycles.
*/
function _resetStrikeReset(address _validator) internal {
uintStorage[keccak256(abi.encodePacked("strikeReset", _validator))] = 0;
}
function getStrikeReset(address _validator) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("strikeReset", _validator))];
}
function getStrikes(address _validator) public view returns(uint256) {
return uintStorage[keccak256(abi.encodePacked("strikeCount", _validator))];
}
}
// File: contracts/Consensus.sol
pragma solidity ^0.4.24;
/**
* @title Contract handling consensus logic
* @author LiorRabin
*/
contract Consensus is ConsensusUtils {
/**
* @dev Function to be called on contract initialization
* @param _initialValidator address of the initial validator. If not set - msg.sender will be the initial validator
*/
function initialize(address _initialValidator) external onlyOwner {
require(!isInitialized());
_setSystemAddress(0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE);
_setCurrentCycle();
if (_initialValidator == address(0)) {
_currentValidatorsAdd(msg.sender);
} else {
_currentValidatorsAdd(_initialValidator);
}
_setFinalized(true);
setInitialized(true);
}
/**
* @dev Function which returns the current validator addresses
*/
function getValidators() external view returns(address[]) {
return currentValidators();
}
/**
* @dev See ValidatorSet.finalizeChange
*/
function finalizeChange() external onlySystem notFinalized {
if (newValidatorSetLength() > 0) {
_setCurrentValidators(newValidatorSet());
emit ChangeFinalized(currentValidators());
}
_setFinalized(true);
}
/**
* @dev Fallback function allowing to pay to this contract. Whoever sends funds is considered as "staking" and wanting to become a validator.
*/
function () external payable {
_delegate(msg.sender, msg.value, msg.sender);
}
/**
* @dev stake to become a validator.
*/
function stake() external payable {
_delegate(msg.sender, msg.value, msg.sender);
}
/**
* @dev delegate to a validator
* @param _validator the address of the validator msg.sender is delegating to
*/
function delegate(address _validator) external payable {
_delegate(msg.sender, msg.value, _validator);
}
/**
* @dev Function to be called when a staker whishes to withdraw some of his staked funds
* @param _amount the amount msg.sender wishes to withdraw from the contract
*/
function withdraw(uint256 _amount) external {
_withdraw(msg.sender, _amount, msg.sender);
}
/**
* @dev Function to be called when a delegator whishes to withdraw some of his staked funds for a validator
* @param _validator the address of the validator msg.sender has delegating to
* @param _amount the amount msg.sender wishes to withdraw from the contract
*/
function withdraw(address _validator, uint256 _amount) external {
_withdraw(msg.sender, _amount, _validator);
}
/**
* @dev Function to be called by the block reward contract each block to handle cycles and snapshots logic
*/
function cycle(address _validator) external onlyBlockReward {
_incBlockCounter(_validator);
if (_hasCycleEnded()) {
IVoting(ProxyStorage(getProxyStorage()).getVoting()).onCycleEnd(currentValidators());
_setCurrentCycle();
_checkJail(currentValidators());
address[] memory newSet = pendingValidators();
if (newSet.length > 0) {
_setNewValidatorSet(newSet);
}
if (newValidatorSetLength() > 0) {
_setFinalized(false);
_setShouldEmitInitiateChange(true);
emit ShouldEmitInitiateChange();
}
IBlockReward(ProxyStorage(getProxyStorage()).getBlockReward()).onCycleEnd();
}
}
/**
* @dev Function to be called by validators only to emit InitiateChange event (only if `shouldEmitInitiateChange` returns true)
*/
function emitInitiateChange() external onlyValidator {
require(shouldEmitInitiateChange());
require(newValidatorSetLength() > 0);
emit InitiateChange(blockhash(block.number - 1), newValidatorSet());
_setShouldEmitInitiateChange(false);
}
/**
* @dev Function to be called by validators to update the validator fee, that's the fee cut the validator takes from his delegatots.
* @param _amount fee percentage when 1e18 represents 100%.
*/
function setValidatorFee(uint256 _amount) external onlyValidator {
require (_amount <= 1 * DECIMALS);
require(_amount >= getMinValidatorFee());
_setValidatorFee(msg.sender, _amount);
}
/**
* @dev Function to be called by jailed validator, in order to be released from jail
*/
function unJail() external onlyJailedValidator {
require(getReleaseBlock(msg.sender) <= getCurrentCycleEndBlock());
_removeFromJail(msg.sender);
}
/**
* @dev Function to be called by current validators to be dropped from the next cycle in order to perform maintenance
*/
function maintenance() external onlyValidator {
require(isJailed(msg.sender) == false);
_maintenance(msg.sender);
}
}

Contract ABI

[{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"uint256","name":""}],"name":"getVersion","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[{"type":"bool","name":""}],"name":"upgradeTo","inputs":[{"type":"address","name":"_newImplementation"}],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"bool","name":""}],"name":"isInitialized","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"renounceOwnership","inputs":[],"constant":false},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getOwner","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getImplementation","inputs":[],"constant":true},{"type":"function","stateMutability":"view","payable":false,"outputs":[{"type":"address","name":""}],"name":"getProxyStorage","inputs":[],"constant":true},{"type":"function","stateMutability":"nonpayable","payable":false,"outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"_newOwner"}],"constant":false},{"type":"constructor","stateMutability":"nonpayable","payable":false,"inputs":[{"type":"address","name":"_proxyStorage"},{"type":"address","name":"_implementation"}]},{"type":"fallback","stateMutability":"payable","payable":true},{"type":"event","name":"Upgraded","inputs":[{"type":"uint256","name":"version","indexed":false},{"type":"address","name":"implementation","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipRenounced","inputs":[{"type":"address","name":"previousOwner","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","indexed":true},{"type":"address","name":"newOwner","indexed":true}],"anonymous":false}]
            

Deployed ByteCode

0x60806040526004361061008d5763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630d8e6e2c81146100d25780633659cfe6146100f9578063392e53cd1461012e578063715018a614610143578063893d20e81461015a578063aaf10f421461018b578063ec15a5e6146101a0578063f2fde38b146101b5575b60006100976101d6565b9050600160a060020a03811615156100ae57600080fd5b3660008037600080366000845af43d6000803e8080156100cd573d6000f35b3d6000fd5b3480156100de57600080fd5b506100e76101e5565b60408051918252519081900360200190f35b34801561010557600080fd5b5061011a600160a060020a03600435166101eb565b604080519115158252519081900360200190f35b34801561013a57600080fd5b5061011a6102b6565b34801561014f57600080fd5b5061015861036e565b005b34801561016657600080fd5b5061016f6103d3565b60408051600160a060020a039092168252519081900360200190f35b34801561019757600080fd5b5061016f6101d6565b3480156101ac57600080fd5b5061016f610491565b3480156101c157600080fd5b50610158600160a060020a0360043516610505565b600154600160a060020a031690565b60005490565b6000806101f6610491565b600160a060020a0316331461020a57600080fd5b600160a060020a038316151561022357600091506102b0565b82600160a060020a03166102356101d6565b600160a060020a0316141561024d57600091506102b0565b6102556101e5565b600101905061026381610589565b61026c8361058e565b604080518281529051600160a060020a038516917f4289d6195cf3c2d2174adf98d0e19d4d2d08887995b99cb7b100e7ffe795820e919081900360200190a2600191505b50919050565b60006006600060405160200180807f6973496e697469616c697a656400000000000000000000000000000000000000815250600d0190506040516020818303038152906040526040518082805190602001908083835b6020831061032b5780518252601f19909201916020918201910161030c565b51815160209384036101000a600019018019909216911617905260408051929094018290039091208652850195909552929092016000205460ff16949350505050565b6103766103d3565b600160a060020a0316331461038a57600080fd5b6103926103d3565b600160a060020a03167ff8df31144d9c2f0f6b59d69b8b98abd5459d07f2742c4df920b25aae33c6482060405160405180910390a26103d160006105bd565b565b60006004600060405160200180807f6f776e657200000000000000000000000000000000000000000000000000000081525060050190506040516020818303038152906040526040518082805190602001908083835b602083106104485780518252601f199092019160209182019101610429565b51815160209384036101000a6000190180199092169116179052604080519290940182900390912086528501959095529290920160002054600160a060020a0316949350505050565b60006004600060405160200180807f70726f787953746f726167650000000000000000000000000000000000000000815250600c019050604051602081830303815290604052604051808280519060200190808383602083106104485780518252601f199092019160209182019101610429565b61050d6103d3565b600160a060020a0316331461052157600080fd5b600160a060020a038116151561053657600080fd5b80600160a060020a03166105486103d3565b600160a060020a03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3610586816105bd565b50565b600055565b6001805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a0392909216919091179055565b806004600060405160200180807f6f776e657200000000000000000000000000000000000000000000000000000081525060050190506040516020818303038152906040526040518082805190602001908083835b602083106106315780518252601f199092019160209182019101610612565b51815160209384036101000a60001901801990921691161790526040805192909401829003909120865285019590955292909201600020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039590951694909417909355505050505600a165627a7a72305820f4416296485a3f71a48dda88ce8128683c8d17cbc70da0bb70aca8575af7400c0029