ETH Price: $2,520.91 (-4.45%)

Contract

0x54653C50da197E3191a0f01AF3d3f9d541ecf49F
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Batch Commit118804342021-02-18 11:01:221352 days ago1613646082IN
0x54653C50...541ecf49F
0 ETH0.0524432132
Batch Reveal110507822020-10-14 1:20:281479 days ago1602638428IN
0x54653C50...541ecf49F
0 ETH0.0035301153
Batch Commit110468132020-10-13 10:42:401480 days ago1602585760IN
0x54653C50...541ecf49F
0 ETH0.0042754856

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
110467882020-10-13 10:36:561480 days ago1602585416  Contract Creation0 ETH
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xe9a3ed88...145748f20
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
DesignatedVoting

Compiler Version
v0.5.13+commit.5b0b510c

Optimization Enabled:
No with 200 runs

Other Settings:
default evmVersion, GNU GPLv3 license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2020-01-15
*/

pragma solidity ^0.5.0;

pragma experimental ABIEncoderV2;

contract EncryptedSender {
    struct TopicData {
        // An (optional) public key used to encrypt messages for this topic. This is only necessary if the sender will
        // not have access to the public key offchain.
        bytes publicKey;

        // The encrypted message.
        bytes message;
    }

    struct Recipient {
        // This maps from a hash to the data for this topic.
        // Note: the hash is a hash of the "subject" or "topic" of the message.
        mapping(bytes32 => TopicData) topics;

        // This contains the set of all authorized senders for this recipient.
        mapping(address => bool) authorizedSenders;
    }

    mapping(address => Recipient) private recipients;

    /**
     * @notice Authorizes `sender` to send messages to the caller.
     */
    function addAuthorizedSender(address sender) external {
        recipients[msg.sender].authorizedSenders[sender] = true;
    }

    /**
     * @notice Revokes `sender`'s authorization to send messages to the caller.
     */
    function removeAuthorizedSender(address sender) external {
        recipients[msg.sender].authorizedSenders[sender] = false;
    }

    /**
     * @notice Gets the current stored message corresponding to `recipient` and `topicHash`.
     * @dev To decrypt messages (this requires access to the recipient's private keys), use the decryptMessage()
     * function in common/Crypto.js.
     */
    function getMessage(address recipient, bytes32 topicHash) external view returns (bytes memory) {
        return recipients[recipient].topics[topicHash].message;
    }

    /**
     * @notice Gets the stored public key for a particular `recipient` and `topicHash`. Return value will be 0 length
     * if no public key has been set.
     * @dev Senders may need this public key to encrypt messages that only the `recipient` can read. If the public key
     * is communicated offchain, this field may be left empty.
     */
    function getPublicKey(address recipient, bytes32 topicHash) external view returns (bytes memory) {
        return recipients[recipient].topics[topicHash].publicKey;
    }

    /**
     * @notice Sends `message` to `recipient_` categorized by a particular `topicHash`. This will overwrite any
     * previous messages sent to this `recipient` with this `topicHash`.
     * @dev To construct an encrypted message, use the encryptMessage() in common/Crypto.js.
     * The public key for the recipient can be obtained using the getPublicKey() method.
     */
    function sendMessage(address recipient_, bytes32 topicHash, bytes memory message) public {
        Recipient storage recipient = recipients[recipient_];
        require(isAuthorizedSender(msg.sender, recipient_), "Not authorized to send to this recipient");
        recipient.topics[topicHash].message = message;
    }

    function removeMessage(address recipient_, bytes32 topicHash) public {
        Recipient storage recipient = recipients[recipient_];
        require(isAuthorizedSender(msg.sender, recipient_), "Not authorized to remove message");
        delete recipient.topics[topicHash].message;
    }

    /**
     * @notice Sets the public key for this caller and topicHash.
     * @dev Note: setting the public key is optional - if the publicKey is communicated or can be derived offchain by
     * the sender, there is no need to set it here. Because there are no specific requirements for the publicKey, there
     * is also no verification of its validity other than its length.
     */
    function setPublicKey(bytes memory publicKey, bytes32 topicHash) public {
        require(publicKey.length == 64, "Public key is the wrong length");
        recipients[msg.sender].topics[topicHash].publicKey = publicKey;
    }

    /**
     * @notice Returns true if the `sender` is authorized to send to the `recipient`.
     */
    function isAuthorizedSender(address sender, address recipient) public view returns (bool) {
        // Note: the recipient is always authorized to send messages to themselves.
        return recipients[recipient].authorizedSenders[sender] || recipient == sender;
    }
}

library FixedPoint {

    using SafeMath for uint;

    // Supports 18 decimals. E.g., 1e18 represents "1", 5e17 represents "0.5".
    // Can represent a value up to (2^256 - 1)/10^18 = ~10^59. 10^59 will be stored internally as uint 10^77.
    uint private constant FP_SCALING_FACTOR = 10**18;

    struct Unsigned {
        uint rawValue;
    }

    /** @dev Constructs an `Unsigned` from an unscaled uint, e.g., `b=5` gets stored internally as `5**18`. */
    function fromUnscaledUint(uint a) internal pure returns (Unsigned memory) {
        return Unsigned(a.mul(FP_SCALING_FACTOR));
    }

    /** @dev Whether `a` is greater than `b`. */
    function isGreaterThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
        return a.rawValue > b.rawValue;
    }

    /** @dev Whether `a` is greater than `b`. */
    function isGreaterThan(Unsigned memory a, uint b) internal pure returns (bool) {
        return a.rawValue > fromUnscaledUint(b).rawValue;
    }

    /** @dev Whether `a` is greater than `b`. */
    function isGreaterThan(uint a, Unsigned memory b) internal pure returns (bool) {
        return fromUnscaledUint(a).rawValue > b.rawValue;
    }

    /** @dev Whether `a` is less than `b`. */
    function isLessThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
        return a.rawValue < b.rawValue;
    }

    /** @dev Whether `a` is less than `b`. */
    function isLessThan(Unsigned memory a, uint b) internal pure returns (bool) {
        return a.rawValue < fromUnscaledUint(b).rawValue;
    }

    /** @dev Whether `a` is less than `b`. */
    function isLessThan(uint a, Unsigned memory b) internal pure returns (bool) {
        return fromUnscaledUint(a).rawValue < b.rawValue;
    }

    /** @dev Adds two `Unsigned`s, reverting on overflow. */
    function add(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return Unsigned(a.rawValue.add(b.rawValue));
    }

    /** @dev Adds an `Unsigned` to an unscaled uint, reverting on overflow. */
    function add(Unsigned memory a, uint b) internal pure returns (Unsigned memory) {
        return add(a, fromUnscaledUint(b));
    }

    /** @dev Subtracts two `Unsigned`s, reverting on underflow. */
    function sub(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return Unsigned(a.rawValue.sub(b.rawValue));
    }

    /** @dev Subtracts an unscaled uint from an `Unsigned`, reverting on underflow. */
    function sub(Unsigned memory a, uint b) internal pure returns (Unsigned memory) {
        return sub(a, fromUnscaledUint(b));
    }

    /** @dev Subtracts an `Unsigned` from an unscaled uint, reverting on underflow. */
    function sub(uint a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return sub(fromUnscaledUint(a), b);
    }

    /** @dev Multiplies two `Unsigned`s, reverting on overflow. */
    function mul(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        // There are two caveats with this computation:
        // 1. Max output for the represented number is ~10^41, otherwise an intermediate value overflows. 10^41 is
        // stored internally as a uint ~10^59.
        // 2. Results that can't be represented exactly are truncated not rounded. E.g., 1.4 * 2e-18 = 2.8e-18, which
        // would round to 3, but this computation produces the result 2.
        // No need to use SafeMath because FP_SCALING_FACTOR != 0.
        return Unsigned(a.rawValue.mul(b.rawValue) / FP_SCALING_FACTOR);
    }

    /** @dev Multiplies an `Unsigned` by an unscaled uint, reverting on overflow. */
    function mul(Unsigned memory a, uint b) internal pure returns (Unsigned memory) {
        return Unsigned(a.rawValue.mul(b));
    }

    /** @dev Divides with truncation two `Unsigned`s, reverting on overflow or division by 0. */
    function div(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        // There are two caveats with this computation:
        // 1. Max value for the number dividend `a` represents is ~10^41, otherwise an intermediate value overflows.
        // 10^41 is stored internally as a uint 10^59.
        // 2. Results that can't be represented exactly are truncated not rounded. E.g., 2 / 3 = 0.6 repeating, which
        // would round to 0.666666666666666667, but this computation produces the result 0.666666666666666666.
        return Unsigned(a.rawValue.mul(FP_SCALING_FACTOR).div(b.rawValue));
    }

    /** @dev Divides with truncation an `Unsigned` by an unscaled uint, reverting on division by 0. */
    function div(Unsigned memory a, uint b) internal pure returns (Unsigned memory) {
        return Unsigned(a.rawValue.div(b));
    }

    /** @dev Divides with truncation an unscaled uint by an `Unsigned`, reverting on overflow or division by 0. */
    function div(uint a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return div(fromUnscaledUint(a), b);
    }

    /** @dev Raises an `Unsigned` to the power of an unscaled uint, reverting on overflow. E.g., `b=2` squares `a`. */
    function pow(Unsigned memory a, uint b) internal pure returns (Unsigned memory output) {
        // TODO(ptare): Consider using the exponentiation by squaring technique instead:
        // https://en.wikipedia.org/wiki/Exponentiation_by_squaring
        output = fromUnscaledUint(1);
        for (uint i = 0; i < b; i = i.add(1)) {
            output = mul(output, a);
        }
    }
}

library Exclusive {
    struct RoleMembership {
        address member;
    }

    function isMember(RoleMembership storage roleMembership, address memberToCheck) internal view returns (bool) {
        return roleMembership.member == memberToCheck;
    }

    function resetMember(RoleMembership storage roleMembership, address newMember) internal {
        require(newMember != address(0x0), "Cannot set an exclusive role to 0x0");
        roleMembership.member = newMember;
    }

    function getMember(RoleMembership storage roleMembership) internal view returns (address) {
        return roleMembership.member;
    }

    function init(RoleMembership storage roleMembership, address initialMember) internal {
        resetMember(roleMembership, initialMember);
    }
}

library Shared {
    struct RoleMembership {
        mapping(address => bool) members;
    }

    function isMember(RoleMembership storage roleMembership, address memberToCheck) internal view returns (bool) {
        return roleMembership.members[memberToCheck];
    }

    function addMember(RoleMembership storage roleMembership, address memberToAdd) internal {
        roleMembership.members[memberToAdd] = true;
    }

    function removeMember(RoleMembership storage roleMembership, address memberToRemove) internal {
        roleMembership.members[memberToRemove] = false;
    }

    function init(RoleMembership storage roleMembership, address[] memory initialMembers) internal {
        for (uint i = 0; i < initialMembers.length; i++) {
            addMember(roleMembership, initialMembers[i]);
        }
    }
}

contract MultiRole {
    using Exclusive for Exclusive.RoleMembership;
    using Shared for Shared.RoleMembership;

    enum RoleType { Invalid, Exclusive, Shared }

    struct Role {
        uint managingRole;
        RoleType roleType;
        Exclusive.RoleMembership exclusiveRoleMembership;
        Shared.RoleMembership sharedRoleMembership;
    }

    mapping(uint => Role) private roles;

    /**
     * @notice Reverts unless the caller is a member of the specified roleId.
     */
    modifier onlyRoleHolder(uint roleId) {
        require(holdsRole(roleId, msg.sender), "Sender does not hold required role");
        _;
    }

    /**
     * @notice Reverts unless the caller is a member of the manager role for the specified roleId.
     */
    modifier onlyRoleManager(uint roleId) {
        require(holdsRole(roles[roleId].managingRole, msg.sender), "Can only be called by a role manager");
        _;
    }

    /**
     * @notice Reverts unless the roleId represents an initialized, exclusive roleId.
     */
    modifier onlyExclusive(uint roleId) {
        require(roles[roleId].roleType == RoleType.Exclusive, "Must be called on an initialized Exclusive role");
        _;
    }

    /**
     * @notice Reverts unless the roleId represents an initialized, shared roleId.
     */
    modifier onlyShared(uint roleId) {
        require(roles[roleId].roleType == RoleType.Shared, "Must be called on an initialized Shared role");
        _;
    }

    /**
     * @notice Whether `memberToCheck` is a member of roleId.
     * @dev Reverts if roleId does not correspond to an initialized role.
     */
    function holdsRole(uint roleId, address memberToCheck) public view returns (bool) {
        Role storage role = roles[roleId];
        if (role.roleType == RoleType.Exclusive) {
            return role.exclusiveRoleMembership.isMember(memberToCheck);
        } else if (role.roleType == RoleType.Shared) {
            return role.sharedRoleMembership.isMember(memberToCheck);
        }
        require(false, "Invalid roleId");
    }

    /**
     * @notice Changes the exclusive role holder of `roleId` to `newMember`.
     * @dev Reverts if the caller is not a member of the managing role for `roleId` or if `roleId` is not an
     * initialized, exclusive role.
     */
    function resetMember(uint roleId, address newMember) public onlyExclusive(roleId) onlyRoleManager(roleId) {
        roles[roleId].exclusiveRoleMembership.resetMember(newMember);
    }

    /**
     * @notice Gets the current holder of the exclusive role, `roleId`.
     * @dev Reverts if `roleId` does not represent an initialized, exclusive role.
     */
    function getMember(uint roleId) public view onlyExclusive(roleId) returns (address) {
        return roles[roleId].exclusiveRoleMembership.getMember();
    }

    /**
     * @notice Adds `newMember` to the shared role, `roleId`.
     * @dev Reverts if `roleId` does not represent an initialized, shared role or if the caller is not a member of the
     * managing role for `roleId`.
     */
    function addMember(uint roleId, address newMember) public onlyShared(roleId) onlyRoleManager(roleId) {
        roles[roleId].sharedRoleMembership.addMember(newMember);
    }

    /**
     * @notice Removes `memberToRemove` from the shared role, `roleId`.
     * @dev Reverts if `roleId` does not represent an initialized, shared role or if the caller is not a member of the
     * managing role for `roleId`.
     */
    function removeMember(uint roleId, address memberToRemove) public onlyShared(roleId) onlyRoleManager(roleId) {
        roles[roleId].sharedRoleMembership.removeMember(memberToRemove);
    }

    /**
     * @notice Reverts if `roleId` is not initialized.
     */
    modifier onlyValidRole(uint roleId) {
        require(roles[roleId].roleType != RoleType.Invalid, "Attempted to use an invalid roleId");
        _;
    }

    /**
     * @notice Reverts if `roleId` is initialized.
     */
    modifier onlyInvalidRole(uint roleId) {
        require(roles[roleId].roleType == RoleType.Invalid, "Cannot use a pre-existing role");
        _;
    }

    /**
     * @notice Internal method to initialize a shared role, `roleId`, which will be managed by `managingRoleId`.
     * `initialMembers` will be immediately added to the role.
     * @dev Should be called by derived contracts, usually at construction time. Will revert if the role is already
     * initialized.
     */
    function _createSharedRole(uint roleId, uint managingRoleId, address[] memory initialMembers)
        internal
        onlyInvalidRole(roleId)
    {
        Role storage role = roles[roleId];
        role.roleType = RoleType.Shared;
        role.managingRole = managingRoleId;
        role.sharedRoleMembership.init(initialMembers);
        require(roles[managingRoleId].roleType != RoleType.Invalid,
            "Attempted to use an invalid role to manage a shared role");
    }

    /**
     * @notice Internal method to initialize a exclusive role, `roleId`, which will be managed by `managingRoleId`.
     * `initialMembers` will be immediately added to the role.
     * @dev Should be called by derived contracts, usually at construction time. Will revert if the role is already
     * initialized.
     */
    function _createExclusiveRole(uint roleId, uint managingRoleId, address initialMember)
        internal
        onlyInvalidRole(roleId)
    {
        Role storage role = roles[roleId];
        role.roleType = RoleType.Exclusive;
        role.managingRole = managingRoleId;
        role.exclusiveRoleMembership.init(initialMember);
        require(roles[managingRoleId].roleType != RoleType.Invalid,
            "Attempted to use an invalid role to manage an exclusive role");
    }
}

interface OracleInterface {

    /**
     * @notice Enqueues a request (if a request isn't already present) for the given `identifier`, `time` pair.
     * @dev Returns the time at which the user should expect the price to be resolved. 0 means the price has already
     * been resolved.
     */
    function requestPrice(bytes32 identifier, uint time) external returns (uint expectedTime);

    /**
     * @notice Whether the Oracle provides prices for this identifier.
     */
    function isIdentifierSupported(bytes32 identifier) external view returns (bool);

    /**
     * @notice Whether the price for `identifier` and `time` is available.
     */
    function hasPrice(bytes32 identifier, uint time) external view returns (bool);

    /**
     * @notice Gets the price for `identifier` and `time` if it has already been requested and resolved.
     * @dev If the price is not available, the method reverts.
     */
    function getPrice(bytes32 identifier, uint time) external view returns (int price);
}

interface RegistryInterface {
    /**
     * @dev Registers a new derivative. Only authorized derivative creators can call this method.
     */
    function registerDerivative(address[] calldata counterparties, address derivativeAddress) external;

    /**
     * @dev Returns whether the derivative has been registered with the registry (and is therefore an authorized.
     * participant in the UMA system).
     */
    function isDerivativeRegistered(address derivative) external view returns (bool isRegistered);

    /**
     * @dev Returns a list of all derivatives that are associated with a particular party.
     */
    function getRegisteredDerivatives(address party) external view returns (address[] memory derivatives);

    /**
     * @dev Returns all registered derivatives.
     */
    function getAllRegisteredDerivatives() external view returns (address[] memory derivatives);
}

contract Registry is RegistryInterface, MultiRole {

    using SafeMath for uint;

    enum Roles {
        // The owner manages the set of DerivativeCreators.
        Owner,
        // Can register derivatives.
        DerivativeCreator
    }

    // Array of all derivatives that are approved to use the UMA Oracle.
    address[] private registeredDerivatives;

    // This enum is required because a WasValid state is required to ensure that derivatives cannot be re-registered.
    enum PointerValidity {
        Invalid,
        Valid
    }

    struct Pointer {
        PointerValidity valid;
        uint128 index;
    }

    // Maps from derivative address to a pointer that refers to that registered derivative in `registeredDerivatives`.
    mapping(address => Pointer) private derivativePointers;

    // Note: this must be stored outside of `registeredDerivatives` because mappings cannot be deleted and copied
    // like normal data. This could be stored in the Pointer struct, but storing it there would muddy the purpose
    // of the Pointer struct and break separation of concern between referential data and data.
    struct PartiesMap {
        mapping(address => bool) parties;
    }

    // Maps from derivative address to the set of parties that are involved in that derivative.
    mapping(address => PartiesMap) private derivativesToParties;

    event NewDerivativeRegistered(address indexed derivativeAddress, address indexed creator, address[] parties);

    constructor() public {
        _createExclusiveRole(uint(Roles.Owner), uint(Roles.Owner), msg.sender);
        // Start with no derivative creators registered.
        _createSharedRole(uint(Roles.DerivativeCreator), uint(Roles.Owner), new address[](0));
    }

    function registerDerivative(address[] calldata parties, address derivativeAddress)
        external
        onlyRoleHolder(uint(Roles.DerivativeCreator))
    {
        // Create derivative pointer.
        Pointer storage pointer = derivativePointers[derivativeAddress];

        // Ensure that the pointer was not valid in the past (derivatives cannot be re-registered or double
        // registered).
        require(pointer.valid == PointerValidity.Invalid);
        pointer.valid = PointerValidity.Valid;

        registeredDerivatives.push(derivativeAddress);

        // No length check necessary because we should never hit (2^127 - 1) derivatives.
        pointer.index = uint128(registeredDerivatives.length.sub(1));

        // Set up PartiesMap for this derivative.
        PartiesMap storage partiesMap = derivativesToParties[derivativeAddress];
        for (uint i = 0; i < parties.length; i = i.add(1)) {
            partiesMap.parties[parties[i]] = true;
        }

        address[] memory partiesForEvent = parties;
        emit NewDerivativeRegistered(derivativeAddress, msg.sender, partiesForEvent);
    }

    function isDerivativeRegistered(address derivative) external view returns (bool isRegistered) {
        return derivativePointers[derivative].valid == PointerValidity.Valid;
    }

    function getRegisteredDerivatives(address party) external view returns (address[] memory derivatives) {
        // This is not ideal - we must statically allocate memory arrays. To be safe, we make a temporary array as long
        // as registeredDerivatives. We populate it with any derivatives that involve the provided party. Then, we copy
        // the array over to the return array, which is allocated using the correct size. Note: this is done by double
        // copying each value rather than storing some referential info (like indices) in memory to reduce the number
        // of storage reads. This is because storage reads are far more expensive than extra memory space (~100:1).
        address[] memory tmpDerivativeArray = new address[](registeredDerivatives.length);
        uint outputIndex = 0;
        for (uint i = 0; i < registeredDerivatives.length; i = i.add(1)) {
            address derivative = registeredDerivatives[i];
            if (derivativesToParties[derivative].parties[party]) {
                // Copy selected derivative to the temporary array.
                tmpDerivativeArray[outputIndex] = derivative;
                outputIndex = outputIndex.add(1);
            }
        }

        // Copy the temp array to the return array that is set to the correct size.
        derivatives = new address[](outputIndex);
        for (uint j = 0; j < outputIndex; j = j.add(1)) {
            derivatives[j] = tmpDerivativeArray[j];
        }
    }

    function getAllRegisteredDerivatives() external view returns (address[] memory derivatives) {
        return registeredDerivatives;
    }
}

library ResultComputation {

    using FixedPoint for FixedPoint.Unsigned;

    struct Data {
        // Maps price to number of tokens that voted for that price.
        mapping(int => FixedPoint.Unsigned) voteFrequency;
        // The total votes that have been added.
        FixedPoint.Unsigned totalVotes;
        // The price that is the current mode, i.e., the price with the highest frequency in `voteFrequency`.
        int currentMode;
    }

    /**
     * @dev Returns whether the result is resolved, and if so, what value it resolved to. `price` should be ignored if
     * `isResolved` is false.
     * @param minVoteThreshold Minimum number of tokens that must have been voted for the result to be valid. Can be
     * used to enforce a minimum voter participation rate, regardless of how the votes are distributed.
     */
    function getResolvedPrice(Data storage data, FixedPoint.Unsigned memory minVoteThreshold)
        internal
        view
        returns (bool isResolved, int price)
    {
        // TODO(ptare): Figure out where this parameter is supposed to come from.
        FixedPoint.Unsigned memory modeThreshold = FixedPoint.fromUnscaledUint(50).div(100);

        if (data.totalVotes.isGreaterThan(minVoteThreshold) &&
            data.voteFrequency[data.currentMode].div(data.totalVotes).isGreaterThan(modeThreshold)) {
            // `modeThreshold` and `minVoteThreshold` are met, so the current mode is the resolved price.
            isResolved = true;
            price = data.currentMode;
        } else {
            isResolved = false;
        }
    }

    /**
     * @dev Adds a new vote to be used when computing the result.
     */
    function addVote(Data storage data, int votePrice, FixedPoint.Unsigned memory numberTokens)
        internal
    {
        data.totalVotes = data.totalVotes.add(numberTokens);
        data.voteFrequency[votePrice] = data.voteFrequency[votePrice].add(numberTokens);
        if (votePrice != data.currentMode
            && data.voteFrequency[votePrice].isGreaterThan(data.voteFrequency[data.currentMode])) {
            data.currentMode = votePrice;
        }
    }

    /**
     * @dev Checks whether a `voteHash` is considered correct. Should only be called after a vote is resolved, i.e.,
     * via `getResolvedPrice`.
     */
    function wasVoteCorrect(Data storage data, bytes32 voteHash) internal view returns (bool) {
        return voteHash == keccak256(abi.encode(data.currentMode));
    }

    /**
     * @dev Gets the total number of tokens whose votes are considered correct. Should only be called after a vote is
     * resolved, i.e., via `getResolvedPrice`.
     */
    function getTotalCorrectlyVotedTokens(Data storage data)
        internal
        view
        returns (FixedPoint.Unsigned memory)
    {
        return data.voteFrequency[data.currentMode];
    }
}

contract Testable {
    // Is the contract being run on the test network. Note: this variable should be set on construction and never
    // modified.
    bool public isTest;

    uint private currentTime;

    constructor(bool _isTest) internal {
        isTest = _isTest;
        if (_isTest) {
            currentTime = now; // solhint-disable-line not-rely-on-time
        }
    }

    /**
     * @notice Reverts if not running in test mode.
     */
    modifier onlyIfTest {
        require(isTest);
        _;
    }

    /**
     * @notice Sets the current time.
     * @dev Will revert if not running in test mode.
     */
    function setCurrentTime(uint _time) external onlyIfTest {
        currentTime = _time;
    }

    /**
     * @notice Gets the current time. Will return the last time set in `setCurrentTime` if running in test mode.
     * Otherwise, it will return the block timestamp.
     */
    function getCurrentTime() public view returns (uint) {
        if (isTest) {
            return currentTime;
        } else {
            return now; // solhint-disable-line not-rely-on-time
        }
    }
}

library VoteTiming {
    using SafeMath for uint;

    // Note: the phases must be in order. Meaning the first enum value must be the first phase, etc.
    enum Phase {
        Commit,
        Reveal
    }

    // Note: this MUST match the number of values in the enum above.
    uint private constant NUM_PHASES = 2;

    struct Data {
        uint roundId;
        uint roundStartTime;
        uint phaseLength;
    }

    /**
     * @notice Initializes the data object. Sets the phase length based on the input and resets the round id and round
     * start time to 1 and 0 respectively.
     * @dev This method should generally only be run once, but it can also be used to reset the data structure to its
     * initial values.
     */
    function init(Data storage data, uint phaseLength) internal {
        data.phaseLength = phaseLength;
        data.roundId = 1;
        data.roundStartTime = 0;
    }

    /**
     * @notice Gets the most recently stored round ID set by updateRoundId().
     */
    function getLastUpdatedRoundId(Data storage data) internal view returns (uint) {
        return data.roundId;
    }

    /**
     * @notice Determines whether time has advanced far enough to advance to the next voting round and update the
     * stored round id.
     */
    function shouldUpdateRoundId(Data storage data, uint currentTime) internal view returns (bool) {
        (uint roundId,) = _getCurrentRoundIdAndStartTime(data, currentTime);
        return data.roundId != roundId;
    }

    /**
     * @notice Updates the round id. Note: if shouldUpdateRoundId() returns false, this method will have no effect.
     */
    function updateRoundId(Data storage data, uint currentTime) internal {
        (data.roundId, data.roundStartTime) = _getCurrentRoundIdAndStartTime(data, currentTime);
    }

    /**
     * @notice Computes what the stored round id would be if it were updated right now, but this method does not
     * commit the update.
     */
    function computeCurrentRoundId(Data storage data, uint currentTime) internal view returns (uint roundId) {
        (roundId,) = _getCurrentRoundIdAndStartTime(data, currentTime);
    }

    /**
     * @notice Computes the current phase based only on the current time.
     */
    function computeCurrentPhase(Data storage data, uint currentTime) internal view returns (Phase) {
        // This employs some hacky casting. We could make this an if-statement if we're worried about type safety.
        return Phase(currentTime.div(data.phaseLength).mod(NUM_PHASES));
    }

    /**
     * @notice Gets the end time of the current round or any round in the future. Note: this method will revert if
     * the roundId < getLastUpdatedRoundId().
     */
    function computeEstimatedRoundEndTime(Data storage data, uint roundId) internal view returns (uint) {
        // The add(1) is because we want the round end time rather than the start time, so it's really the start of
        // the next round.
        uint roundDiff = roundId.sub(data.roundId).add(1);
        uint roundLength = data.phaseLength.mul(NUM_PHASES);
        return data.roundStartTime.add(roundDiff.mul(roundLength));
    }

    /**
     * @dev Computes an updated round id and round start time based on the current time.
     */
    function _getCurrentRoundIdAndStartTime(Data storage data, uint currentTime)
        private
        view
        returns (uint roundId, uint startTime)
    {
        uint currentStartTime = data.roundStartTime;
        // Return current data if time has moved backwards.
        if (currentTime <= data.roundStartTime) {
            return (data.roundId, data.roundStartTime);
        }

        // Get the start of the round that currentTime would be a part of by flooring by roundLength.
        uint roundLength = data.phaseLength.mul(NUM_PHASES);
        startTime = currentTime.div(roundLength).mul(roundLength);

        // Only increment the round ID if the start time has changed.
        if (startTime > currentStartTime) {
            roundId = data.roundId.add(1);
        } else {
            roundId = data.roundId;
        }
    }
}

contract VotingInterface {
    struct PendingRequest {
        bytes32 identifier;
        uint time;
    }

    /**
     * @notice Commit your vote for a price request for `identifier` at `time`.
     * @dev (`identifier`, `time`) must correspond to a price request that's currently in the commit phase. `hash`
     * should be the keccak256 hash of the price you want to vote for and a `int salt`. Commits can be changed.
     */
    function commitVote(bytes32 identifier, uint time, bytes32 hash) external;

    /**
     * @notice Reveal a previously committed vote for `identifier` at `time`.
     * @dev The revealed `price` and `salt` must match the latest `hash` that `commitVote()` was called with. Only the
     * committer can reveal their vote.
     */
    function revealVote(bytes32 identifier, uint time, int price, int salt) external;

    /**
     * @notice Gets the queries that are being voted on this round.
     */
    function getPendingRequests() external view returns (PendingRequest[] memory);

    /**
     * @notice Gets the current vote phase (commit or reveal) based on the current block time.
     */
    function getVotePhase() external view returns (VoteTiming.Phase);

    /**
     * @notice Gets the current vote round id based on the current block time.
     */
    function getCurrentRoundId() external view returns (uint);

    /**
     * @notice Retrieves rewards owed for a set of resolved price requests.
     */
    function retrieveRewards(address voterAddress, uint roundId, PendingRequest[] memory) public returns
    (FixedPoint.Unsigned memory);
}

contract Withdrawable is MultiRole {

    uint private _roleId;

    /**
     * @notice Withdraws ETH from the contract.
     */
    function withdraw(uint amount) external onlyRoleHolder(_roleId) {
        msg.sender.transfer(amount);
    }

    /**
     * @notice Withdraws ERC20 tokens from the contract.
     */
    function withdrawErc20(address erc20Address, uint amount) external onlyRoleHolder(_roleId) {
        IERC20 erc20 = IERC20(erc20Address);
        require(erc20.transfer(msg.sender, amount));
    }

    /**
     * @notice Internal method that allows derived contracts to create a role for withdrawal.
     * @dev Either this method or `setWithdrawRole` must be called by the derived class for this contract to function
     * properly.
     */
    function createWithdrawRole(uint roleId, uint managingRoleId, address owner) internal {
        _roleId = roleId;
        _createExclusiveRole(roleId, managingRoleId, owner);
    }

    /**
     * @notice Internal method that allows derived contracts to choose the role for withdrawal.
     * @dev The role `roleId` must exist. Either this method or `createWithdrawRole` must be called by the derived class
     * for this contract to function properly.
     */
    function setWithdrawRole(uint roleId) internal {
        _roleId = roleId;
    }
}

contract DesignatedVoting is MultiRole, Withdrawable {

    enum Roles {
        // Can set the Voter and Withdrawer roles.
        Owner,
        // Can vote through this contract.
        Voter
    }

    // Reference to the UMA Finder contract, allowing Voting upgrades to be performed without requiring any calls to
    // this contract.
    Finder private finder;

    constructor(address finderAddress, address ownerAddress, address voterAddress) public {
        _createExclusiveRole(uint(Roles.Owner), uint(Roles.Owner), ownerAddress);
        _createExclusiveRole(uint(Roles.Voter), uint(Roles.Owner), voterAddress);
        setWithdrawRole(uint(Roles.Owner));

        finder = Finder(finderAddress);
    }

    /**
     * @notice Forwards a commit to Voting.
     */
    function commitVote(bytes32 identifier, uint time, bytes32 hash) external onlyRoleHolder(uint(Roles.Voter)) {
        _getVotingAddress().commitVote(identifier, time, hash);
    }

    /**
     * @notice Forwards a batch commit to Voting.
     */
    function batchCommit(Voting.Commitment[] calldata commits) external onlyRoleHolder(uint(Roles.Voter)) {
        _getVotingAddress().batchCommit(commits);
    }

    /**
     * @notice Forwards a reveal to Voting.
     */
    function revealVote(bytes32 identifier, uint time, int price, int salt) external onlyRoleHolder(uint(Roles.Voter)) {
        _getVotingAddress().revealVote(identifier, time, price, salt);
    }

    /**
     * @notice Forwards a batch reveal to Voting.
     */
    function batchReveal(Voting.Reveal[] calldata reveals) external onlyRoleHolder(uint(Roles.Voter)) {
        _getVotingAddress().batchReveal(reveals);
    }

    /**
     * @notice Forwards a reward retrieval to Voting.
     */
    function retrieveRewards(uint roundId, VotingInterface.PendingRequest[] memory toRetrieve)
        public
        onlyRoleHolder(uint(Roles.Voter))
        returns (FixedPoint.Unsigned memory rewardsIssued)
    {
        return _getVotingAddress().retrieveRewards(address(this), roundId, toRetrieve);
    }

    function _getVotingAddress() private view returns (Voting) {
        return Voting(finder.getImplementationAddress("Oracle"));
    }
}

contract Context {
    // Empty internal constructor, to prevent people from mistakenly deploying
    // an instance of this contract, which should be used via inheritance.
    constructor () internal { }
    // solhint-disable-previous-line no-empty-blocks

    function _msgSender() internal view returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

library Counters {
    using SafeMath for uint256;

    struct Counter {
        // This variable should never be directly accessed by users of the library: interactions must be restricted to
        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
        // this feature: see https://github.com/ethereum/solidity/issues/4637
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        counter._value += 1;
    }

    function decrement(Counter storage counter) internal {
        counter._value = counter._value.sub(1);
    }
}

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 Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    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);
    }
}

library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot 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-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        _owner = _msgSender();
        emit OwnershipTransferred(address(0), _owner);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return _msgSender() == _owner;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

contract Finder is Ownable {

    mapping(bytes32 => address) public interfacesImplemented;

    event InterfaceImplementationChanged(bytes32 indexed interfaceName, address indexed newImplementationAddress);

    /**
     * @dev Updates the address of the contract that implements `interfaceName`.
     */
    function changeImplementationAddress(bytes32 interfaceName, address implementationAddress)
        external
        onlyOwner
    {
        interfacesImplemented[interfaceName] = implementationAddress;
        emit InterfaceImplementationChanged(interfaceName, implementationAddress);
    }
    
    /**
     * @dev Gets the address of the contract that implements the given `interfaceName`.
     */
    function getImplementationAddress(bytes32 interfaceName)
        external
        view
        returns (address implementationAddress)
    {
        implementationAddress = interfacesImplemented[interfaceName];
        require(implementationAddress != address(0x0), "No implementation for interface found");
    }
}

contract Voting is Testable, Ownable, OracleInterface, VotingInterface, EncryptedSender {
    using FixedPoint for FixedPoint.Unsigned;
    using SafeMath for uint;
    using VoteTiming for VoteTiming.Data;
    using ResultComputation for ResultComputation.Data;

    // Identifies a unique price request for which the Oracle will always return the same value.
    // Tracks ongoing votes as well as the result of the vote.
    struct PriceRequest {
        bytes32 identifier;
        uint time;

        // A map containing all votes for this price in various rounds.
        mapping(uint => VoteInstance) voteInstances;

        // If in the past, this was the voting round where this price was resolved. If current or the upcoming round,
        // this is the voting round where this price will be voted on, but not necessarily resolved.
        uint lastVotingRound;

        // The index in the `pendingPriceRequests` that references this PriceRequest. A value of UINT_MAX means that
        // this PriceRequest is resolved and has been cleaned up from `pendingPriceRequests`.
        uint index;
    }

    struct VoteInstance {
        // Maps (voterAddress) to their submission.
        mapping(address => VoteSubmission) voteSubmissions;

        // The data structure containing the computed voting results.
        ResultComputation.Data resultComputation;
    }

    struct VoteSubmission {
        // A bytes32 of `0` indicates no commit or a commit that was already revealed.
        bytes32 commit;

        // The hash of the value that was revealed.
        // Note: this is only used for computation of rewards.
        bytes32 revealHash;
    }

    // Captures the necessary data for making a commitment.
    // Used as a parameter when making batch commitments.
    // Not used as a data structure for storage.
    struct Commitment {
        bytes32 identifier;

        uint time;

        bytes32 hash;

        bytes encryptedVote;
    }

    // Captures the necessary data for revealing a vote.
    // Used as a parameter when making batch reveals.
    // Not used as a data structure for storage.
    struct Reveal {
        bytes32 identifier;

        uint time;

        int price;

        int salt;
    }

    struct Round {
        // Voting token snapshot ID for this round. If this is 0, no snapshot has been taken.
        uint snapshotId;

        // Inflation rate set for this round.
        FixedPoint.Unsigned inflationRate;
    }

    // Represents the status a price request has.
    enum RequestStatus {
        // Was never requested.
        NotRequested,
        // Is being voted on in the current round.
        Active,
        // Was resolved in a previous round.
        Resolved,
        // Is scheduled to be voted on in a future round.
        Future
    }

    // Maps round numbers to the rounds.
    mapping(uint => Round) private rounds;

    // Maps price request IDs to the PriceRequest struct.
    mapping(bytes32 => PriceRequest) private priceRequests;

    // Price request ids for price requests that haven't yet been marked as resolved. These requests may be for future
    // rounds.
    bytes32[] private pendingPriceRequests;

    VoteTiming.Data private voteTiming;

    // The set of identifiers the oracle can provide verified prices for.
    mapping(bytes32 => bool) private supportedIdentifiers;

    // Percentage of the total token supply that must be used in a vote to create a valid price resolution.
    // 1 == 100%.
    FixedPoint.Unsigned private gatPercentage;

    // Global setting for the rate of inflation per vote. This is the percentage of the snapshotted total supply that
    // should be split among the correct voters. Note: this value is used to set per-round inflation at the beginning
    // of each round.
    // 1 = 100%
    FixedPoint.Unsigned private inflationRate;

    // Reference to the voting token.
    VotingToken private votingToken;

    // Reference to the Finder.
    Finder private finder;

    // If non-zero, this contract has been migrated to this address. All voters and financial contracts should query the
    // new address only.
    address private migratedAddress;

    // Max value of an unsigned integer.
    uint constant private UINT_MAX = ~uint(0);

    event VoteCommitted(address indexed voter, uint indexed roundId, bytes32 indexed identifier, uint time);

    event VoteRevealed(
        address indexed voter,
        uint indexed roundId,
        bytes32 indexed identifier,
        uint time,
        int price,
        uint numTokens
    );

    event RewardsRetrieved(address indexed voter, uint indexed roundId, bytes32 indexed identifier, uint time,
        uint numTokens);

    event PriceRequestAdded(uint indexed votingRoundId, bytes32 indexed identifier, uint time);

    event PriceResolved(uint indexed resolutionRoundId, bytes32 indexed identifier, uint time, int price);

    event SupportedIdentifierAdded(bytes32 indexed identifier);

    event SupportedIdentifierRemoved(bytes32 indexed identifier);

    /**
     * @notice Construct the Voting contract.
     * @param phaseLength length of the commit and reveal phases in seconds.
     * @param _gatPercentage percentage of the total token supply that must be used in a vote to create a valid price
     * resolution.
     * @param _isTest whether this contract is being constructed for the purpose of running automated tests.
     */
    constructor(
        uint phaseLength,
        FixedPoint.Unsigned memory _gatPercentage,
        FixedPoint.Unsigned memory _inflationRate,
        address _votingToken,
        address _finder,
        bool _isTest
    ) public Testable(_isTest) {
        voteTiming.init(phaseLength);
        // TODO(#779): GAT percentage must be < 100%
        require(_gatPercentage.isLessThan(1));
        gatPercentage = _gatPercentage;
        inflationRate = _inflationRate;
        votingToken = VotingToken(_votingToken);
        finder = Finder(_finder);
    }

    modifier onlyRegisteredDerivative() {
        if (migratedAddress != address(0)) {
            require(msg.sender == migratedAddress);
        } else {
            Registry registry = Registry(finder.getImplementationAddress("Registry"));
            // TODO(#779): Must be registered derivative
            require(registry.isDerivativeRegistered(msg.sender));
        }
        _;
    }

    modifier onlyIfNotMigrated() {
        require(migratedAddress == address(0));
        _;
    }

    function requestPrice(bytes32 identifier, uint time)
        external
        onlyRegisteredDerivative()
        returns (uint expectedTime)
    {
        uint blockTime = getCurrentTime();
        // TODO(#779): Price request must be for a time in the past
        require(time <= blockTime);
        // TODO(#779): Price request for unsupported identifier
        require(supportedIdentifiers[identifier]);

        // Must ensure the round is updated here so the requested price will be voted on in the next commit cycle.
        _updateRound(blockTime);

        bytes32 priceRequestId = _encodePriceRequest(identifier, time);
        PriceRequest storage priceRequest = priceRequests[priceRequestId];
        uint currentRoundId = voteTiming.computeCurrentRoundId(blockTime);

        RequestStatus requestStatus = _getRequestStatus(priceRequest, currentRoundId);
        if (requestStatus == RequestStatus.Active) {
            return voteTiming.computeEstimatedRoundEndTime(currentRoundId);
        } else if (requestStatus == RequestStatus.Resolved) {
            return 0;
        } else if (requestStatus == RequestStatus.Future) {
            return voteTiming.computeEstimatedRoundEndTime(priceRequest.lastVotingRound);
        }

        // Price has never been requested.
        // Price requests always go in the next round, so add 1 to the computed current round.
        uint nextRoundId = currentRoundId.add(1);

        priceRequests[priceRequestId] = PriceRequest({
            identifier: identifier,
            time: time,
            lastVotingRound: nextRoundId,
            index: pendingPriceRequests.length
        });
        pendingPriceRequests.push(priceRequestId);
        emit PriceRequestAdded(nextRoundId, identifier, time);

        // Estimate the end of next round and return the time.
        return voteTiming.computeEstimatedRoundEndTime(nextRoundId);
    }

    function batchCommit(Commitment[] calldata commits) external {
        for (uint i = 0; i < commits.length; i++) {
            if (commits[i].encryptedVote.length == 0) {
                commitVote(commits[i].identifier, commits[i].time, commits[i].hash);
            } else {
                commitAndPersistEncryptedVote(
                    commits[i].identifier,
                    commits[i].time,
                    commits[i].hash,
                    commits[i].encryptedVote);
            }
        }
    }

    function batchReveal(Reveal[] calldata reveals) external {
        for (uint i = 0; i < reveals.length; i++) {
            revealVote(reveals[i].identifier, reveals[i].time, reveals[i].price, reveals[i].salt);
        }
    }

    /**
     * @notice Disables this Voting contract in favor of the migrated one.
     */
    function setMigrated(address newVotingAddress) external onlyOwner {
        migratedAddress = newVotingAddress;
    }

    /**
     * @notice Adds the provided identifier as a supported identifier. Price requests using this identifier will be
     * succeed after this call.
     */
    function addSupportedIdentifier(bytes32 identifier) external onlyOwner {
        if (!supportedIdentifiers[identifier]) {
            supportedIdentifiers[identifier] = true;
            emit SupportedIdentifierAdded(identifier);
        }
    }

    /**
     * @notice Removes the identifier from the whitelist. Price requests using this identifier will no longer succeed
     * after this call.
     */
    function removeSupportedIdentifier(bytes32 identifier) external onlyOwner {
        if (supportedIdentifiers[identifier]) {
            supportedIdentifiers[identifier] = false;
            emit SupportedIdentifierRemoved(identifier);
        }
    }

    function isIdentifierSupported(bytes32 identifier) external view returns (bool) {
        return supportedIdentifiers[identifier];
    }

    function hasPrice(bytes32 identifier, uint time) external view onlyRegisteredDerivative() returns (bool _hasPrice) {
        (_hasPrice, ,) = _getPriceOrError(identifier, time);
    }

    function getPrice(bytes32 identifier, uint time) external view onlyRegisteredDerivative() returns (int) {
        (bool _hasPrice, int price, string memory message) = _getPriceOrError(identifier, time);

        // TODO(#779): If the price wasn't available, revert with the provided message.
        require(_hasPrice, message);
        return price;
    }

    function getPendingRequests() external view returns (PendingRequest[] memory pendingRequests) {
        uint blockTime = getCurrentTime();
        uint currentRoundId = voteTiming.computeCurrentRoundId(blockTime);

        // Solidity memory arrays aren't resizable (and reading storage is expensive). Hence this hackery to filter
        // `pendingPriceRequests` only to those requests that `isActive()`.
        PendingRequest[] memory unresolved = new PendingRequest[](pendingPriceRequests.length);
        uint numUnresolved = 0;

        for (uint i = 0; i < pendingPriceRequests.length; i++) {
            PriceRequest storage priceRequest = priceRequests[pendingPriceRequests[i]];
            if (_getRequestStatus(priceRequest, currentRoundId) == RequestStatus.Active) {
                unresolved[numUnresolved] = PendingRequest(
                    { identifier: priceRequest.identifier, time: priceRequest.time });
                numUnresolved++;
            }
        }

        pendingRequests = new PendingRequest[](numUnresolved);
        for (uint i = 0; i < numUnresolved; i++) {
            pendingRequests[i] = unresolved[i];
        }
    }

    function getVotePhase() external view returns (VoteTiming.Phase) {
        return voteTiming.computeCurrentPhase(getCurrentTime());
    }

    function getCurrentRoundId() external view returns (uint) {
        return voteTiming.computeCurrentRoundId(getCurrentTime());
    }

    function commitVote(bytes32 identifier, uint time, bytes32 hash) public onlyIfNotMigrated() {
        // TODO(#779): Committed hash of 0 is disallowed, choose a different salt
        require(hash != bytes32(0));

        // Current time is required for all vote timing queries.
        uint blockTime = getCurrentTime();
        // TODO(#779): Cannot commit while in the reveal phase
        require(voteTiming.computeCurrentPhase(blockTime) == VoteTiming.Phase.Commit);

        // Should only update the round in the commit phase because a new round that's already in the reveal phase
        // would be wasted.
        _updateRound(blockTime);

        // At this point, the computed and last updated round ID should be equal.
        uint currentRoundId = voteTiming.computeCurrentRoundId(blockTime);

        PriceRequest storage priceRequest = _getPriceRequest(identifier, time);
        // TODO(#779): Cannot commit on inactive request
        require(_getRequestStatus(priceRequest, currentRoundId) == RequestStatus.Active);

        priceRequest.lastVotingRound = currentRoundId;
        VoteInstance storage voteInstance = priceRequest.voteInstances[currentRoundId];
        voteInstance.voteSubmissions[msg.sender].commit = hash;

        emit VoteCommitted(msg.sender, currentRoundId, identifier, time);
    }

    function revealVote(bytes32 identifier, uint time, int price, int salt) public onlyIfNotMigrated() {
        uint blockTime = getCurrentTime();
        require(voteTiming.computeCurrentPhase(blockTime) == VoteTiming.Phase.Reveal,
            "Cannot reveal while in the commit phase");

        // Note: computing the current round is required to disallow people from revealing an old commit after the
        // round is over.
        uint roundId = voteTiming.computeCurrentRoundId(blockTime);

        PriceRequest storage priceRequest = _getPriceRequest(identifier, time);
        VoteInstance storage voteInstance = priceRequest.voteInstances[roundId];
        VoteSubmission storage voteSubmission = voteInstance.voteSubmissions[msg.sender];

        // 0 hashes are disallowed in the commit phase, so they indicate a different error.
        require(voteSubmission.commit != bytes32(0), "Cannot reveal an uncommitted or previously revealed hash");
        require(keccak256(abi.encode(price, salt)) == voteSubmission.commit,
                "Committed hash doesn't match revealed price and salt");
        delete voteSubmission.commit;

        // Get or create a snapshot for this round.
        uint snapshotId = _getOrCreateSnapshotId(roundId);

        // Get the voter's snapshotted balance. Since balances are returned pre-scaled by 10**18, we can directly
        // initialize the Unsigned value with the returned uint.
        FixedPoint.Unsigned memory balance = FixedPoint.Unsigned(votingToken.balanceOfAt(msg.sender, snapshotId));

        // Set the voter's submission.
        voteSubmission.revealHash = keccak256(abi.encode(price));

        // Add vote to the results.
        voteInstance.resultComputation.addVote(price, balance);

        // Remove the stored message for this price request, if it exists.
        bytes32 topicHash = keccak256(abi.encode(identifier, time, roundId));
        removeMessage(msg.sender, topicHash);

        emit VoteRevealed(msg.sender, roundId, identifier, time, price, balance.rawValue);
    }

    function commitAndPersistEncryptedVote(
        bytes32 identifier,
        uint time,
        bytes32 hash,
        bytes memory encryptedVote
    ) public {
        commitVote(identifier, time, hash);

        uint roundId = voteTiming.computeCurrentRoundId(getCurrentTime());
        bytes32 topicHash = keccak256(abi.encode(identifier, time, roundId));
        sendMessage(msg.sender, topicHash, encryptedVote);
    }

    /**
     * @notice Resets the inflation rate. Note: this change only applies to rounds that have not yet begun.
     * @dev This method is public because calldata structs are not currently supported by solidity.
     */
    function setInflationRate(FixedPoint.Unsigned memory _inflationRate) public onlyOwner {
        inflationRate = _inflationRate;
    }

    function retrieveRewards(address voterAddress, uint roundId, PendingRequest[] memory toRetrieve)
        public
        returns (FixedPoint.Unsigned memory totalRewardToIssue)
    {
        if (migratedAddress != address(0)) {
            require(msg.sender == migratedAddress);
        }
        uint blockTime = getCurrentTime();
        _updateRound(blockTime);
        require(roundId < voteTiming.computeCurrentRoundId(blockTime));

        Round storage round = rounds[roundId];
        FixedPoint.Unsigned memory snapshotBalance = FixedPoint.Unsigned(
            votingToken.balanceOfAt(voterAddress, round.snapshotId));

        // Compute the total amount of reward that will be issued for each of the votes in the round.
        FixedPoint.Unsigned memory snapshotTotalSupply = FixedPoint.Unsigned(
            votingToken.totalSupplyAt(round.snapshotId));
        FixedPoint.Unsigned memory totalRewardPerVote = round.inflationRate.mul(snapshotTotalSupply);

        // Keep track of the voter's accumulated token reward.
        totalRewardToIssue = FixedPoint.Unsigned(0);

        for (uint i = 0; i < toRetrieve.length; i++) {
            PriceRequest storage priceRequest = _getPriceRequest(toRetrieve[i].identifier, toRetrieve[i].time);
            VoteInstance storage voteInstance = priceRequest.voteInstances[priceRequest.lastVotingRound];

            require(priceRequest.lastVotingRound == roundId, "Only retrieve rewards for votes resolved in same round");

            _resolvePriceRequest(priceRequest, voteInstance);

            if (voteInstance.resultComputation.wasVoteCorrect(voteInstance.voteSubmissions[voterAddress].revealHash)) {
                // The price was successfully resolved during the voter's last voting round, the voter revealed and was
                // correct, so they are elgible for a reward.
                FixedPoint.Unsigned memory correctTokens = voteInstance.resultComputation.
                    getTotalCorrectlyVotedTokens();

                // Compute the reward and add to the cumulative reward.
                FixedPoint.Unsigned memory reward = snapshotBalance.mul(totalRewardPerVote).div(correctTokens);
                totalRewardToIssue = totalRewardToIssue.add(reward);

                // Emit reward retrieval for this vote.
                emit RewardsRetrieved(voterAddress, roundId, toRetrieve[i].identifier, toRetrieve[i].time,
                    reward.rawValue);
            } else {
                // Emit a 0 token retrieval on incorrect votes.
                emit RewardsRetrieved(voterAddress, roundId, toRetrieve[i].identifier, toRetrieve[i].time, 0);
            }

            // Delete the submission to capture any refund and clean up storage.
            delete voteInstance.voteSubmissions[voterAddress].revealHash;
        }

        // Issue any accumulated rewards.
        if (totalRewardToIssue.isGreaterThan(0)) {
            require(votingToken.mint(voterAddress, totalRewardToIssue.rawValue));
        }
    }

    /*
     * @dev Checks to see if there is a price that has or can be resolved for an (identifier, time) pair.
     * @returns a boolean noting whether a price is resolved, the price, and an error string if necessary.
     */
    function _getPriceOrError(bytes32 identifier, uint time)
        private
        view
        returns (bool _hasPrice, int price, string memory err)
    {
        PriceRequest storage priceRequest = _getPriceRequest(identifier, time);
        uint currentRoundId = voteTiming.computeCurrentRoundId(getCurrentTime());

        RequestStatus requestStatus = _getRequestStatus(priceRequest, currentRoundId);
        if (requestStatus == RequestStatus.Active) {
            return (false, 0, "The current voting round has not ended");
        } else if (requestStatus == RequestStatus.Resolved) {
            VoteInstance storage voteInstance = priceRequest.voteInstances[priceRequest.lastVotingRound];
            (, int resolvedPrice) = voteInstance.resultComputation.getResolvedPrice(
                _computeGat(priceRequest.lastVotingRound));
            return (true, resolvedPrice, "");
        } else if (requestStatus == RequestStatus.Future) {
            return (false, 0, "Price will be voted on in the future");
        } else {
            return (false, 0, "Price was never requested");
        }
    }

    function _getPriceRequest(bytes32 identifier, uint time) private view returns (PriceRequest storage) {
        return priceRequests[_encodePriceRequest(identifier, time)];
    }

    function _encodePriceRequest(bytes32 identifier, uint time) private pure returns (bytes32) {
        return keccak256(abi.encode(identifier, time));
    }

    function _getOrCreateSnapshotId(uint roundId) private returns (uint) {
        Round storage round = rounds[roundId];
        if (round.snapshotId == 0) {
            // There is no snapshot ID set, so create one.
            round.snapshotId = votingToken.snapshot();
        }

        return round.snapshotId;
    }

    function _resolvePriceRequest(PriceRequest storage priceRequest, VoteInstance storage voteInstance) private {
        if (priceRequest.index == UINT_MAX) {
            return;
        }
        (bool isResolved, int resolvedPrice) = voteInstance.resultComputation.getResolvedPrice(
            _computeGat(priceRequest.lastVotingRound));
        require(isResolved, "Can't resolve an unresolved price request");

        // Delete the resolved price request from pendingPriceRequests.
        uint lastIndex = pendingPriceRequests.length - 1;
        PriceRequest storage lastPriceRequest = priceRequests[pendingPriceRequests[lastIndex]];
        lastPriceRequest.index = priceRequest.index;
        pendingPriceRequests[priceRequest.index] = pendingPriceRequests[lastIndex];
        delete pendingPriceRequests[lastIndex];

        priceRequest.index = UINT_MAX;
        emit PriceResolved(priceRequest.lastVotingRound, priceRequest.identifier, priceRequest.time, resolvedPrice);
    }

    function _updateRound(uint blockTime) private {
        if (!voteTiming.shouldUpdateRoundId(blockTime)) {
            return;
        }
        uint nextVotingRoundId = voteTiming.computeCurrentRoundId(blockTime);

        // Set the round inflation rate to the current global inflation rate.
        rounds[nextVotingRoundId].inflationRate = inflationRate;

        // Update the stored round to the current one.
        voteTiming.updateRoundId(blockTime);
    }

    function _computeGat(uint roundId) private view returns (FixedPoint.Unsigned memory) {
        uint snapshotId = rounds[roundId].snapshotId;
        if (snapshotId == 0) {
            // No snapshot - return max value to err on the side of caution.
            return FixedPoint.Unsigned(UINT_MAX);
        }

        // Grab the snaphotted supply from the voting token. It's already scaled by 10**18, so we can directly
        // initialize the Unsigned value with the returned uint.
        FixedPoint.Unsigned memory snapshottedSupply = FixedPoint.Unsigned(votingToken.totalSupplyAt(snapshotId));

        // Multiply the total supply at the snapshot by the gatPercentage to get the GAT in number of tokens.
        return snapshottedSupply.mul(gatPercentage);
    }

    function _getRequestStatus(PriceRequest storage priceRequest, uint currentRoundId)
        private
        view
        returns (RequestStatus)
    {
        if (priceRequest.lastVotingRound == 0) {
            return RequestStatus.NotRequested;
        } else if (priceRequest.lastVotingRound < currentRoundId) {
            VoteInstance storage voteInstance = priceRequest.voteInstances[priceRequest.lastVotingRound];
            (bool isResolved, ) = voteInstance.resultComputation.getResolvedPrice(
                _computeGat(priceRequest.lastVotingRound));
            return isResolved ? RequestStatus.Resolved : RequestStatus.Active;
        } else if (priceRequest.lastVotingRound == currentRoundId) {
            return RequestStatus.Active;
        } else {
            // Means than priceRequest.lastVotingRound > currentRoundId
            return RequestStatus.Future;
        }
    }
}

interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: 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
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract ExpandedIERC20 is IERC20 {
    /**
     * @notice Burns a specific amount of the caller's tokens.
     * @dev Only burns the caller's tokens, so it is safe to leave this method permissionless.
     */
    function burn(uint value) external;

    /**
     * @notice Mints tokens and adds them to the balance of the `to` address.
     * @dev This method should be permissioned to only allow designated parties to mint tokens.
     */
    function mint(address to, uint value) external returns (bool);
}

contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

    mapping (address => mapping (address => uint256)) private _allowances;

    uint256 private _totalSupply;

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20};
     *
     * Requirements:
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for `sender`'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to the zero address");

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }

     /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: burn from the zero address");

        _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
     *
     * This is internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
     * from the caller's allowance.
     *
     * See {_burn} and {_approve}.
     */
    function _burnFrom(address account, uint256 amount) internal {
        _burn(account, amount);
        _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
    }
}

contract ERC20Snapshot is ERC20 {
    using SafeMath for uint256;
    using Arrays for uint256[];
    using Counters for Counters.Counter;

    // Snapshotted values have arrays of ids and the value corresponding to that id. These could be an array of a
    // Snapshot struct, but that would impede usage of functions that work on an array.
    struct Snapshots {
        uint256[] ids;
        uint256[] values;
    }

    mapping (address => Snapshots) private _accountBalanceSnapshots;
    Snapshots private _totalSupplySnapshots;

    // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid.
    Counters.Counter private _currentSnapshotId;

    event Snapshot(uint256 id);

    // Creates a new snapshot id. Balances are only stored in snapshots on demand: unless a snapshot was taken, a
    // balance change will not be recorded. This means the extra added cost of storing snapshotted balances is only paid
    // when required, but is also flexible enough that it allows for e.g. daily snapshots.
    function snapshot() public returns (uint256) {
        _currentSnapshotId.increment();

        uint256 currentId = _currentSnapshotId.current();
        emit Snapshot(currentId);
        return currentId;
    }

    function balanceOfAt(address account, uint256 snapshotId) public view returns (uint256) {
        (bool snapshotted, uint256 value) = _valueAt(snapshotId, _accountBalanceSnapshots[account]);

        return snapshotted ? value : balanceOf(account);
    }

    function totalSupplyAt(uint256 snapshotId) public view returns(uint256) {
        (bool snapshotted, uint256 value) = _valueAt(snapshotId, _totalSupplySnapshots);

        return snapshotted ? value : totalSupply();
    }

    // _transfer, _mint and _burn are the only functions where the balances are modified, so it is there that the
    // snapshots are updated. Note that the update happens _before_ the balance change, with the pre-modified value.
    // The same is true for the total supply and _mint and _burn.
    function _transfer(address from, address to, uint256 value) internal {
        _updateAccountSnapshot(from);
        _updateAccountSnapshot(to);

        super._transfer(from, to, value);
    }

    function _mint(address account, uint256 value) internal {
        _updateAccountSnapshot(account);
        _updateTotalSupplySnapshot();

        super._mint(account, value);
    }

    function _burn(address account, uint256 value) internal {
        _updateAccountSnapshot(account);
        _updateTotalSupplySnapshot();

        super._burn(account, value);
    }

    // When a valid snapshot is queried, there are three possibilities:
    //  a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never
    //  created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds
    //  to this id is the current one.
    //  b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the
    //  requested id, and its value is the one to return.
    //  c) More snapshots were created after the requested one, and the queried value was later modified. There will be
    //  no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is
    //  larger than the requested one.
    //
    // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if
    // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does
    // exactly this.
    function _valueAt(uint256 snapshotId, Snapshots storage snapshots)
        private view returns (bool, uint256)
    {
        require(snapshotId > 0, "ERC20Snapshot: id is 0");
        // solhint-disable-next-line max-line-length
        require(snapshotId <= _currentSnapshotId.current(), "ERC20Snapshot: nonexistent id");

        uint256 index = snapshots.ids.findUpperBound(snapshotId);

        if (index == snapshots.ids.length) {
            return (false, 0);
        } else {
            return (true, snapshots.values[index]);
        }
    }

    function _updateAccountSnapshot(address account) private {
        _updateSnapshot(_accountBalanceSnapshots[account], balanceOf(account));
    }

    function _updateTotalSupplySnapshot() private {
        _updateSnapshot(_totalSupplySnapshots, totalSupply());
    }

    function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private {
        uint256 currentId = _currentSnapshotId.current();
        if (_lastSnapshotId(snapshots.ids) < currentId) {
            snapshots.ids.push(currentId);
            snapshots.values.push(currentValue);
        }
    }

    function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) {
        if (ids.length == 0) {
            return 0;
        } else {
            return ids[ids.length - 1];
        }
    }
}

contract VotingToken is ExpandedIERC20, ERC20Snapshot, MultiRole {

    enum Roles {
        // Can set the minter and burner.
        Owner,
        // Addresses that can mint new tokens.
        Minter,
        // Addresses that can burn tokens that address owns.
        Burner
    }

    // Standard ERC20 metadata.
    string public constant name = "UMA Voting Token v1"; // solhint-disable-line const-name-snakecase
    string public constant symbol = "UMA"; // solhint-disable-line const-name-snakecase
    uint8 public constant decimals = 18; // solhint-disable-line const-name-snakecase

    constructor() public {
        _createExclusiveRole(uint(Roles.Owner), uint(Roles.Owner), msg.sender);
        _createSharedRole(uint(Roles.Minter), uint(Roles.Owner), new address[](0));
        _createSharedRole(uint(Roles.Burner), uint(Roles.Owner), new address[](0));
    }

    /**
     * @dev Mints `value` tokens to `recipient`, returning true on success.
     */
    function mint(address recipient, uint value) external onlyRoleHolder(uint(Roles.Minter)) returns (bool) {
        _mint(recipient, value);
        return true;
    }

    /**
     * @dev Burns `value` tokens owned by `msg.sender`.
     */
    function burn(uint value) external onlyRoleHolder(uint(Roles.Burner)) {
        _burn(msg.sender, value);
    }
}

library Arrays {
   /**
     * @dev Searches a sorted `array` and returns the first index that contains
     * a value greater or equal to `element`. If no such index exists (i.e. all
     * values in the array are strictly less than `element`), the array length is
     * returned. Time complexity O(log n).
     *
     * `array` is expected to be sorted in ascending order, and to contain no
     * repeated elements.
     */
    function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
        if (array.length == 0) {
            return 0;
        }

        uint256 low = 0;
        uint256 high = array.length;

        while (low < high) {
            uint256 mid = Math.average(low, high);

            // Note that mid will always be strictly less than high (i.e. it will be a valid array index)
            // because Math.average rounds down (it does integer division with truncation).
            if (array[mid] > element) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }

        // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
        if (low > 0 && array[low - 1] == element) {
            return low - 1;
        } else {
            return low;
        }
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"finderAddress","type":"address"},{"internalType":"address","name":"ownerAddress","type":"address"},{"internalType":"address","name":"voterAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":false,"inputs":[{"internalType":"uint256","name":"roleId","type":"uint256"},{"internalType":"address","name":"newMember","type":"address"}],"name":"addMember","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"bytes","name":"encryptedVote","type":"bytes"}],"internalType":"struct Voting.Commitment[]","name":"commits","type":"tuple[]"}],"name":"batchCommit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"int256","name":"price","type":"int256"},{"internalType":"int256","name":"salt","type":"int256"}],"internalType":"struct Voting.Reveal[]","name":"reveals","type":"tuple[]"}],"name":"batchReveal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"bytes32","name":"hash","type":"bytes32"}],"name":"commitVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"roleId","type":"uint256"}],"name":"getMember","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"roleId","type":"uint256"},{"internalType":"address","name":"memberToCheck","type":"address"}],"name":"holdsRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"roleId","type":"uint256"},{"internalType":"address","name":"memberToRemove","type":"address"}],"name":"removeMember","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"roleId","type":"uint256"},{"internalType":"address","name":"newMember","type":"address"}],"name":"resetMember","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"roundId","type":"uint256"},{"components":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"}],"internalType":"struct VotingInterface.PendingRequest[]","name":"toRetrieve","type":"tuple[]"}],"name":"retrieveRewards","outputs":[{"components":[{"internalType":"uint256","name":"rawValue","type":"uint256"}],"internalType":"struct FixedPoint.Unsigned","name":"rewardsIssued","type":"tuple"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"time","type":"uint256"},{"internalType":"int256","name":"price","type":"int256"},{"internalType":"int256","name":"salt","type":"int256"}],"name":"revealVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"erc20Address","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawErc20","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100b45760003560e01c806374d0a6761161007157806374d0a676146101755780637cdc1cb914610191578063ab3545e5146101c1578063bd1f4b52146101f1578063d8651ad01461020d578063d97c05be14610229576100b4565b80632e1a7d4d146100b95780633756a796146100d55780634c7a2603146101055780636852eea0146101215780636be7658b1461013d57806370a0cf2c14610159575b600080fd5b6100d360048036036100ce91908101906113f5565b610245565b005b6100ef60048036036100ea919081019061145a565b6102dc565b6040516100fc9190611cb8565b60405180910390f35b61011f600480360361011a9190810190611369565b6103d8565b005b61013b600480360361013691908101906112ac565b6104ac565b005b6101576004803603610152919081019061141e565b61057a565b005b610173600480360361016e9190810190611267565b610681565b005b61018f600480360361018a919081019061141e565b61074f565b005b6101ab60048036036101a6919081019061141e565b610856565b6040516101b89190611b48565b60405180910390f35b6101db60048036036101d691908101906113f5565b610951565b6040516101e89190611a7e565b60405180910390f35b61020b6004803603610206919081019061122b565b6109f4565b005b6102276004803603610222919081019061131a565b610ae1565b005b610243600480360361023e919081019061141e565b610bb2565b005b6001546102528133610856565b610291576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161028890611c38565b60405180910390fd5b3373ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f193505050501580156102d7573d6000803e3d6000fd5b505050565b6102e4610fc1565b6001808111156102f057fe5b6102fa8133610856565b610339576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161033090611c38565b60405180910390fd5b610341610cba565b73ffffffffffffffffffffffffffffffffffffffff16630d434e7e3086866040518463ffffffff1660e01b815260040161037d93929190611ac2565b602060405180830381600087803b15801561039757600080fd5b505af11580156103ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506103cf91908101906113cc565b91505092915050565b6001808111156103e457fe5b6103ee8133610856565b61042d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161042490611c38565b60405180910390fd5b610435610cba565b73ffffffffffffffffffffffffffffffffffffffff16634c7a2603868686866040518563ffffffff1660e01b81526004016104739493929190611b9a565b600060405180830381600087803b15801561048d57600080fd5b505af11580156104a1573d6000803e3d6000fd5b505050505050505050565b6001808111156104b857fe5b6104c28133610856565b610501576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104f890611c38565b60405180910390fd5b610509610cba565b73ffffffffffffffffffffffffffffffffffffffff16636852eea084846040518363ffffffff1660e01b8152600401610543929190611b24565b600060405180830381600087803b15801561055d57600080fd5b505af1158015610571573d6000803e3d6000fd5b50505050505050565b8160028081111561058757fe5b60008083815260200190815260200160002060010160009054906101000a900460ff1660028111156105b557fe5b146105f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ec90611c18565b60405180910390fd5b826106156000808381526020019081526020016000206000015433610856565b610654576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161064b90611c78565b60405180910390fd5b61067b83600080878152602001908152602001600020600301610d6a90919063ffffffff16565b50505050565b60018081111561068d57fe5b6106978133610856565b6106d6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106cd90611c38565b60405180910390fd5b6106de610cba565b73ffffffffffffffffffffffffffffffffffffffff166370a0cf2c84846040518363ffffffff1660e01b8152600401610718929190611b00565b600060405180830381600087803b15801561073257600080fd5b505af1158015610746573d6000803e3d6000fd5b50505050505050565b8160028081111561075c57fe5b60008083815260200190815260200160002060010160009054906101000a900460ff16600281111561078a57fe5b146107ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107c190611c18565b60405180910390fd5b826107ea6000808381526020019081526020016000206000015433610856565b610829576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161082090611c78565b60405180910390fd5b61085083600080878152602001908152602001600020600301610dc890919063ffffffff16565b50505050565b60008060008085815260200190815260200160002090506001600281111561087a57fe5b8160010160009054906101000a900460ff16600281111561089757fe5b14156108bb576108b38382600201610e2690919063ffffffff16565b91505061094b565b6002808111156108c757fe5b8160010160009054906101000a900460ff1660028111156108e457fe5b1415610908576109008382600301610e8390919063ffffffff16565b91505061094b565b6000610949576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161094090611c98565b60405180910390fd5b505b92915050565b6000816001600281111561096157fe5b60008083815260200190815260200160002060010160009054906101000a900460ff16600281111561098f57fe5b146109cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109c690611bf8565b60405180910390fd5b6109ec600080858152602001908152602001600020600201610edc565b915050919050565b600154610a018133610856565b610a40576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610a3790611c38565b60405180910390fd5b60008390508073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb33856040518363ffffffff1660e01b8152600401610a80929190611a99565b602060405180830381600087803b158015610a9a57600080fd5b505af1158015610aae573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610ad291908101906112f1565b610adb57600080fd5b50505050565b600180811115610aed57fe5b610af78133610856565b610b36576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b2d90611c38565b60405180910390fd5b610b3e610cba565b73ffffffffffffffffffffffffffffffffffffffff1663d8651ad08585856040518463ffffffff1660e01b8152600401610b7a93929190611b63565b600060405180830381600087803b158015610b9457600080fd5b505af1158015610ba8573d6000803e3d6000fd5b5050505050505050565b8160016002811115610bc057fe5b60008083815260200190815260200160002060010160009054906101000a900460ff166002811115610bee57fe5b14610c2e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c2590611bf8565b60405180910390fd5b82610c4e6000808381526020019081526020016000206000015433610856565b610c8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c8490611c78565b60405180910390fd5b610cb483600080878152602001908152602001600020600201610f0a90919063ffffffff16565b50505050565b6000600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663aafd5e406040518163ffffffff1660e01b8152600401610d1590611bdf565b60206040518083038186803b158015610d2d57600080fd5b505afa158015610d41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d659190810190611202565b905090565b60008260000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505050565b60018260000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505050565b60008173ffffffffffffffffffffffffffffffffffffffff168360000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614905092915050565b60008260000160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b60008160000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610f7a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f7190611c58565b60405180910390fd5b808260000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050565b6040518060200160405280600081525090565b600081359050610fe381611f50565b92915050565b600081519050610ff881611f50565b92915050565b60008083601f84011261101057600080fd5b8235905067ffffffffffffffff81111561102957600080fd5b60208301915083602082028301111561104157600080fd5b9250929050565b600082601f83011261105957600080fd5b813561106c61106782611d00565b611cd3565b9150818183526020840193506020810190508385604084028201111561109157600080fd5b60005b838110156110c157816110a78882611154565b845260208401935060408301925050600181019050611094565b5050505092915050565b60008083601f8401126110dd57600080fd5b8235905067ffffffffffffffff8111156110f657600080fd5b60208301915083608082028301111561110e57600080fd5b9250929050565b60008151905061112481611f67565b92915050565b60008135905061113981611f7e565b92915050565b60008135905061114e81611f95565b92915050565b60006040828403121561116657600080fd5b6111706040611cd3565b905060006111808482850161112a565b6000830152506020611194848285016111d8565b60208301525092915050565b6000602082840312156111b257600080fd5b6111bc6020611cd3565b905060006111cc848285016111ed565b60008301525092915050565b6000813590506111e781611fac565b92915050565b6000815190506111fc81611fac565b92915050565b60006020828403121561121457600080fd5b600061122284828501610fe9565b91505092915050565b6000806040838503121561123e57600080fd5b600061124c85828601610fd4565b925050602061125d858286016111d8565b9150509250929050565b6000806020838503121561127a57600080fd5b600083013567ffffffffffffffff81111561129457600080fd5b6112a085828601610ffe565b92509250509250929050565b600080602083850312156112bf57600080fd5b600083013567ffffffffffffffff8111156112d957600080fd5b6112e5858286016110cb565b92509250509250929050565b60006020828403121561130357600080fd5b600061131184828501611115565b91505092915050565b60008060006060848603121561132f57600080fd5b600061133d8682870161112a565b935050602061134e868287016111d8565b925050604061135f8682870161112a565b9150509250925092565b6000806000806080858703121561137f57600080fd5b600061138d8782880161112a565b945050602061139e878288016111d8565b93505060406113af8782880161113f565b92505060606113c08782880161113f565b91505092959194509250565b6000602082840312156113de57600080fd5b60006113ec848285016111a0565b91505092915050565b60006020828403121561140757600080fd5b6000611415848285016111d8565b91505092915050565b6000806040838503121561143157600080fd5b600061143f858286016111d8565b925050602061145085828601610fd4565b9150509250929050565b6000806040838503121561146d57600080fd5b600061147b858286016111d8565b925050602083013567ffffffffffffffff81111561149857600080fd5b6114a485828601611048565b9150509250929050565b60006114ba838361191b565b905092915050565b60006114ce83836119a0565b60408301905092915050565b60006114e683836119cf565b60808301905092915050565b6114fb81611efa565b82525050565b61150a81611e9e565b82525050565b600061151c8385611d7e565b93508360208402850161152e84611d28565b8060005b878110156115725784840389526115498284611e58565b61155385826114ae565b945061155e83611d57565b925060208a01995050600181019050611532565b50829750879450505050509392505050565b600061158f82611d4c565b6115998185611d8f565b93506115a483611d32565b8060005b838110156115d55781516115bc88826114c2565b97506115c783611d64565b9250506001810190506115a8565b5085935050505092915050565b60006115ee8385611da0565b93506115f982611d42565b8060005b858110156116325761160f8284611e7c565b61161988826114da565b975061162483611d71565b9250506001810190506115fd565b5085925050509392505050565b61164881611eb0565b82525050565b61165781611ebc565b82525050565b61166681611ebc565b82525050565b60006116788385611db1565b9350611685838584611f30565b61168e83611f3f565b840190509392505050565b6116a281611ec6565b82525050565b6116b181611ec6565b82525050565b7f4f7261636c650000000000000000000000000000000000000000000000000000815250565b60006116ea602f83611dc2565b91507f4d7573742062652063616c6c6564206f6e20616e20696e697469616c697a656460008301527f204578636c757369766520726f6c6500000000000000000000000000000000006020830152604082019050919050565b6000611750602c83611dc2565b91507f4d7573742062652063616c6c6564206f6e20616e20696e697469616c697a656460008301527f2053686172656420726f6c6500000000000000000000000000000000000000006020830152604082019050919050565b60006117b6602283611dc2565b91507f53656e64657220646f6573206e6f7420686f6c6420726571756972656420726f60008301527f6c650000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b600061181c602383611dc2565b91507f43616e6e6f742073657420616e206578636c757369766520726f6c6520746f2060008301527f30783000000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611882602483611dc2565b91507f43616e206f6e6c792062652063616c6c6564206279206120726f6c65206d616e60008301527f61676572000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b60006118e8600e83611dc2565b91507f496e76616c696420726f6c6549640000000000000000000000000000000000006000830152602082019050919050565b60006080830161192e6000840184611dd3565b61193b600086018261164e565b506119496020840184611e87565b6119566020860182611a60565b506119646040840184611dd3565b611971604086018261164e565b5061197f6060840184611dea565b858303606087015261199283828461166c565b925050508091505092915050565b6040820160008201516119b6600085018261164e565b5060208201516119c96020850182611a60565b50505050565b608082016119e06000830183611dd3565b6119ed600085018261164e565b506119fb6020830183611e87565b611a086020850182611a60565b50611a166040830183611e41565b611a236040850182611699565b50611a316060830183611e41565b611a3e6060850182611699565b50505050565b602082016000820151611a5a6000850182611a60565b50505050565b611a6981611ef0565b82525050565b611a7881611ef0565b82525050565b6000602082019050611a936000830184611501565b92915050565b6000604082019050611aae60008301856114f2565b611abb6020830184611a6f565b9392505050565b6000606082019050611ad76000830186611501565b611ae46020830185611a6f565b8181036040830152611af68184611584565b9050949350505050565b60006020820190508181036000830152611b1b818486611510565b90509392505050565b60006020820190508181036000830152611b3f8184866115e2565b90509392505050565b6000602082019050611b5d600083018461163f565b92915050565b6000606082019050611b78600083018661165d565b611b856020830185611a6f565b611b92604083018461165d565b949350505050565b6000608082019050611baf600083018761165d565b611bbc6020830186611a6f565b611bc960408301856116a8565b611bd660608301846116a8565b95945050505050565b6000602082019050611bf3600083016116b7565b919050565b60006020820190508181036000830152611c11816116dd565b9050919050565b60006020820190508181036000830152611c3181611743565b9050919050565b60006020820190508181036000830152611c51816117a9565b9050919050565b60006020820190508181036000830152611c718161180f565b9050919050565b60006020820190508181036000830152611c9181611875565b9050919050565b60006020820190508181036000830152611cb1816118db565b9050919050565b6000602082019050611ccd6000830184611a44565b92915050565b6000604051905081810181811067ffffffffffffffff82111715611cf657600080fd5b8060405250919050565b600067ffffffffffffffff821115611d1757600080fd5b602082029050602081019050919050565b6000819050919050565b6000819050602082019050919050565b6000819050919050565b600081519050919050565b6000602082019050919050565b6000602082019050919050565b6000608082019050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b600082825260208201905092915050565b6000611de2602084018461112a565b905092915050565b60008083356001602003843603038112611e0357600080fd5b83810192508235915060208301925067ffffffffffffffff821115611e2757600080fd5b600182023603841315611e3957600080fd5b509250929050565b6000611e50602084018461113f565b905092915050565b600082356001608003833603038112611e7057600080fd5b82810191505092915050565b600082905092915050565b6000611e9660208401846111d8565b905092915050565b6000611ea982611ed0565b9050919050565b60008115159050919050565b6000819050919050565b6000819050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000611f0582611f0c565b9050919050565b6000611f1782611f1e565b9050919050565b6000611f2982611ed0565b9050919050565b82818337600083830152505050565b6000601f19601f8301169050919050565b611f5981611e9e565b8114611f6457600080fd5b50565b611f7081611eb0565b8114611f7b57600080fd5b50565b611f8781611ebc565b8114611f9257600080fd5b50565b611f9e81611ec6565b8114611fa957600080fd5b50565b611fb581611ef0565b8114611fc057600080fd5b50565b611fcd8282610f0a565b505056fea365627a7a723158208db37f5a9ca4dec4c48dbab8e8abe86a3860059359d379938d271801833943436c6578706572696d656e74616cf564736f6c634300050d0040

Deployed Bytecode Sourcemap

35593:2263:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;35593:2263:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;34377:110;;;;;;;;;;;;;;;;:::i;:::-;;37399:312;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;36889:195;;;;;;;;;;;;;;;;:::i;:::-;;37161:157;;;;;;;;;;;;;;;;:::i;:::-;;15155:191;;;;;;;;;;;;;;;;:::i;:::-;;36657:161;;;;;;;;;;;;;;;;:::i;:::-;;14725:175;;;;;;;;;;;;;;;;:::i;:::-;;13261:441;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;14321:159;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;34571:199;;;;;;;;;;;;;;;;:::i;:::-;;36399:181;;;;;;;;;;;;;;;;:::i;:::-;;13953:185;;;;;;;;;;;;;;;;:::i;:::-;;34377:110;34432:7;;12160:29;12170:6;12178:10;12160:9;:29::i;:::-;12152:76;;;;;;;;;;;;;;;;;;;;;;34452:10;:19;;:27;34472:6;34452:27;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;34452:27:0;34377:110;;:::o;37399:312::-;37567:40;;:::i;:::-;37535:11;37530:17;;;;;;;;12160:29;12170:6;12178:10;12160:9;:29::i;:::-;12152:76;;;;;;;;;;;;;;;;;;;;;;37632:19;:17;:19::i;:::-;:35;;;37676:4;37683:7;37692:10;37632:71;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;37632:71:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;37632:71:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;37632:71:0;;;;;;;;;37625:78;;37399:312;;;;;:::o;36889:195::-;36990:11;36985:17;;;;;;;;12160:29;12170:6;12178:10;12160:9;:29::i;:::-;12152:76;;;;;;;;;;;;;;;;;;;;;;37015:19;:17;:19::i;:::-;:30;;;37046:10;37058:4;37064:5;37071:4;37015:61;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;37015:61:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;37015:61:0;;;;36889:195;;;;;:::o;37161:157::-;37245:11;37240:17;;;;;;;;12160:29;12170:6;12178:10;12160:9;:29::i;:::-;12152:76;;;;;;;;;;;;;;;;;;;;;;37270:19;:17;:19::i;:::-;:31;;;37302:7;;37270:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;37270:40:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;37270:40:0;;;;37161:157;;;:::o;15155:191::-;15232:6;13013:15;12987:41;;;;;;;;:5;:13;12993:6;12987:13;;;;;;;;;;;:22;;;;;;;;;;;;:41;;;;;;;;;12979:98;;;;;;;;;;;;;;;;;;;;;;15256:6;12431:49;12441:5;:13;12447:6;12441:13;;;;;;;;;;;:26;;;12469:10;12431:9;:49::i;:::-;12423:98;;;;;;;;;;;;;;;;;;;;;;15275:63;15323:14;15275:5;:13;15281:6;15275:13;;;;;;;;;;;:34;;:47;;:63;;;;:::i;:::-;13088:1;15155:191;;;:::o;36657:161::-;36745:11;36740:17;;;;;;;;12160:29;12170:6;12178:10;12160:9;:29::i;:::-;12152:76;;;;;;;;;;;;;;;;;;;;;;36770:19;:17;:19::i;:::-;:31;;;36802:7;;36770:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;36770:40:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;36770:40:0;;;;36657:161;;;:::o;14725:175::-;14794:6;13013:15;12987:41;;;;;;;;:5;:13;12993:6;12987:13;;;;;;;;;;;:22;;;;;;;;;;;;:41;;;;;;;;;12979:98;;;;;;;;;;;;;;;;;;;;;;14818:6;12431:49;12441:5;:13;12447:6;12441:13;;;;;;;;;;;:26;;;12469:10;12431:9;:49::i;:::-;12423:98;;;;;;;;;;;;;;;;;;;;;;14837:55;14882:9;14837:5;:13;14843:6;14837:13;;;;;;;;;;;:34;;:44;;:55;;;;:::i;:::-;13088:1;14725:175;;;:::o;13261:441::-;13337:4;13354:17;13374:5;:13;13380:6;13374:13;;;;;;;;;;;13354:33;;13419:18;13402:35;;;;;;;;:4;:13;;;;;;;;;;;;:35;;;;;;;;;13398:254;;;13461:52;13499:13;13461:4;:28;;:37;;:52;;;;:::i;:::-;13454:59;;;;;13398:254;13552:15;13535:32;;;;;;;;:4;:13;;;;;;;;;;;;:32;;;;;;;;;13531:121;;;13591:49;13626:13;13591:4;:25;;:34;;:49;;;;:::i;:::-;13584:56;;;;;13531:121;13670:5;13662:32;;;;;;;;;;;;;;;;;;;;;;13261:441;;;;;;:::o;14321:159::-;14396:7;14379:6;12735:18;12709:44;;;;;;;;:5;:13;12715:6;12709:13;;;;;;;;;;;:22;;;;;;;;;;;;:44;;;;;;;;;12701:104;;;;;;;;;;;;;;;;;;;;;;14423:49;:5;:13;14429:6;14423:13;;;;;;;;;;;:37;;:47;:49::i;:::-;14416:56;;14321:159;;;;:::o;34571:199::-;34653:7;;12160:29;12170:6;12178:10;12160:9;:29::i;:::-;12152:76;;;;;;;;;;;;;;;;;;;;;;34673:12;34695;34673:35;;34727:5;:14;;;34742:10;34754:6;34727:34;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;34727:34:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;34727:34:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;34727:34:0;;;;;;;;;34719:43;;;;;;12239:1;34571:199;;;:::o;36399:181::-;36493:11;36488:17;;;;;;;;12160:29;12170:6;12178:10;12160:9;:29::i;:::-;12152:76;;;;;;;;;;;;;;;;;;;;;;36518:19;:17;:19::i;:::-;:30;;;36549:10;36561:4;36567;36518:54;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;36518:54:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;36518:54:0;;;;36399:181;;;;:::o;13953:185::-;14027:6;12735:18;12709:44;;;;;;;;:5;:13;12715:6;12709:13;;;;;;;;;;;:22;;;;;;;;;;;;:44;;;;;;;;;12701:104;;;;;;;;;;;;;;;;;;;;;;14051:6;12431:49;12441:5;:13;12447:6;12441:13;;;;;;;;;;;:26;;;12469:10;12431:9;:49::i;:::-;12423:98;;;;;;;;;;;;;;;;;;;;;;14070:60;14120:9;14070:5;:13;14076:6;14070:13;;;;;;;;;;;:37;;:49;;:60;;;;:::i;:::-;12816:1;13953:185;;;:::o;37719:134::-;37770:6;37803;;;;;;;;;;;:31;;;:41;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;37803:41:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;37803:41:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;37803:41:0;;;;;;;;;37789:56;;37719:134;:::o;11184:159::-;11330:5;11289:14;:22;;:38;11312:14;11289:38;;;;;;;;;;;;;;;;:46;;;;;;;;;;;;;;;;;;11184:159;;:::o;11027:149::-;11164:4;11126:14;:22;;:35;11149:11;11126:35;;;;;;;;;;;;;;;;:42;;;;;;;;;;;;;;;;;;11027:149;;:::o;10033:173::-;10136:4;10185:13;10160:38;;:14;:21;;;;;;;;;;;;:38;;;10153:45;;10033:173;;;;:::o;10847:172::-;10950:4;10974:14;:22;;:37;10997:13;10974:37;;;;;;;;;;;;;;;;;;;;;;;;;10967:44;;10847:172;;;;:::o;10446:137::-;10527:7;10554:14;:21;;;;;;;;;;;;10547:28;;10446:137;;;:::o;10214:224::-;10342:3;10321:25;;:9;:25;;;;10313:73;;;;;;;;;;;;;;;;;;;;;;10421:9;10397:14;:21;;;:33;;;;;;;;;;;;;;;;;;10214:224;;:::o;35593:2263::-;;;;;;;;;;;;;;:::o;5:130:-1:-;;85:6;72:20;63:29;;97:33;124:5;97:33;;;57:78;;;;;142:134;;226:6;220:13;211:22;;238:33;265:5;238:33;;;205:71;;;;;318:378;;;474:3;467:4;459:6;455:17;451:27;441:2;;492:1;489;482:12;441:2;525:6;512:20;502:30;;552:18;544:6;541:30;538:2;;;584:1;581;574:12;538:2;618:4;610:6;606:17;594:29;;669:3;661:4;653:6;649:17;639:8;635:32;632:41;629:2;;;686:1;683;676:12;629:2;434:262;;;;;;752:791;;897:3;890:4;882:6;878:17;874:27;864:2;;915:1;912;905:12;864:2;952:6;939:20;974:108;989:92;1074:6;989:92;;;974:108;;;965:117;;1099:5;1124:6;1117:5;1110:21;1154:4;1146:6;1142:17;1132:27;;1176:4;1171:3;1167:14;1160:21;;1229:6;1276:3;1268:4;1260:6;1256:17;1251:3;1247:27;1244:36;1241:2;;;1293:1;1290;1283:12;1241:2;1318:1;1303:234;1328:6;1325:1;1322:13;1303:234;;;1386:3;1408:65;1469:3;1457:10;1408:65;;;1403:3;1396:78;1497:4;1492:3;1488:14;1481:21;;1525:4;1520:3;1516:14;1509:21;;1360:177;1350:1;1347;1343:9;1338:14;;1303:234;;;1307:14;857:686;;;;;;;;1582:374;;;1734:3;1727:4;1719:6;1715:17;1711:27;1701:2;;1752:1;1749;1742:12;1701:2;1785:6;1772:20;1762:30;;1812:18;1804:6;1801:30;1798:2;;;1844:1;1841;1834:12;1798:2;1878:4;1870:6;1866:17;1854:29;;1929:3;1921:4;1913:6;1909:17;1899:8;1895:32;1892:41;1889:2;;;1946:1;1943;1936:12;1889:2;1694:262;;;;;;1964:128;;2045:6;2039:13;2030:22;;2057:30;2081:5;2057:30;;;2024:68;;;;;2099:130;;2179:6;2166:20;2157:29;;2191:33;2218:5;2191:33;;;2151:78;;;;;2236:128;;2315:6;2302:20;2293:29;;2327:32;2353:5;2327:32;;;2287:77;;;;;2415:475;;2532:4;2520:9;2515:3;2511:19;2507:30;2504:2;;;2550:1;2547;2540:12;2504:2;2568:20;2583:4;2568:20;;;2559:29;;2644:1;2675:49;2720:3;2711:6;2700:9;2696:22;2675:49;;;2669:3;2662:5;2658:15;2651:74;2598:138;2786:2;2819:49;2864:3;2855:6;2844:9;2840:22;2819:49;;;2812:4;2805:5;2801:16;2794:75;2746:134;2498:392;;;;;2930:348;;3055:4;3043:9;3038:3;3034:19;3030:30;3027:2;;;3073:1;3070;3063:12;3027:2;3091:20;3106:4;3091:20;;;3082:29;;3165:1;3196:60;3252:3;3243:6;3232:9;3228:22;3196:60;;;3190:3;3183:5;3179:15;3172:85;3121:147;3021:257;;;;;3285:130;;3365:6;3352:20;3343:29;;3377:33;3404:5;3377:33;;;3337:78;;;;;3422:134;;3506:6;3500:13;3491:22;;3518:33;3545:5;3518:33;;;3485:71;;;;;3563:263;;3678:2;3666:9;3657:7;3653:23;3649:32;3646:2;;;3694:1;3691;3684:12;3646:2;3729:1;3746:64;3802:7;3793:6;3782:9;3778:22;3746:64;;;3736:74;;3708:108;3640:186;;;;;3833:366;;;3954:2;3942:9;3933:7;3929:23;3925:32;3922:2;;;3970:1;3967;3960:12;3922:2;4005:1;4022:53;4067:7;4058:6;4047:9;4043:22;4022:53;;;4012:63;;3984:97;4112:2;4130:53;4175:7;4166:6;4155:9;4151:22;4130:53;;;4120:63;;4091:98;3916:283;;;;;;4206:449;;;4371:2;4359:9;4350:7;4346:23;4342:32;4339:2;;;4387:1;4384;4377:12;4339:2;4450:1;4439:9;4435:17;4422:31;4473:18;4465:6;4462:30;4459:2;;;4505:1;4502;4495:12;4459:2;4533:106;4631:7;4622:6;4611:9;4607:22;4533:106;;;4523:116;;;;4401:244;4333:322;;;;;;4662:441;;;4823:2;4811:9;4802:7;4798:23;4794:32;4791:2;;;4839:1;4836;4829:12;4791:2;4902:1;4891:9;4887:17;4874:31;4925:18;4917:6;4914:30;4911:2;;;4957:1;4954;4947:12;4911:2;4985:102;5079:7;5070:6;5059:9;5055:22;4985:102;;;4975:112;;;;4853:240;4785:318;;;;;;5110:257;;5222:2;5210:9;5201:7;5197:23;5193:32;5190:2;;;5238:1;5235;5228:12;5190:2;5273:1;5290:61;5343:7;5334:6;5323:9;5319:22;5290:61;;;5280:71;;5252:105;5184:183;;;;;5374:491;;;;5512:2;5500:9;5491:7;5487:23;5483:32;5480:2;;;5528:1;5525;5518:12;5480:2;5563:1;5580:53;5625:7;5616:6;5605:9;5601:22;5580:53;;;5570:63;;5542:97;5670:2;5688:53;5733:7;5724:6;5713:9;5709:22;5688:53;;;5678:63;;5649:98;5778:2;5796:53;5841:7;5832:6;5821:9;5817:22;5796:53;;;5786:63;;5757:98;5474:391;;;;;;5872:613;;;;;6025:3;6013:9;6004:7;6000:23;5996:33;5993:2;;;6042:1;6039;6032:12;5993:2;6077:1;6094:53;6139:7;6130:6;6119:9;6115:22;6094:53;;;6084:63;;6056:97;6184:2;6202:53;6247:7;6238:6;6227:9;6223:22;6202:53;;;6192:63;;6163:98;6292:2;6310:52;6354:7;6345:6;6334:9;6330:22;6310:52;;;6300:62;;6271:97;6399:2;6417:52;6461:7;6452:6;6441:9;6437:22;6417:52;;;6407:62;;6378:97;5987:498;;;;;;;;6492:313;;6632:2;6620:9;6611:7;6607:23;6603:32;6600:2;;;6648:1;6645;6638:12;6600:2;6683:1;6700:89;6781:7;6772:6;6761:9;6757:22;6700:89;;;6690:99;;6662:133;6594:211;;;;;6812:241;;6916:2;6904:9;6895:7;6891:23;6887:32;6884:2;;;6932:1;6929;6922:12;6884:2;6967:1;6984:53;7029:7;7020:6;7009:9;7005:22;6984:53;;;6974:63;;6946:97;6878:175;;;;;7060:366;;;7181:2;7169:9;7160:7;7156:23;7152:32;7149:2;;;7197:1;7194;7187:12;7149:2;7232:1;7249:53;7294:7;7285:6;7274:9;7270:22;7249:53;;;7239:63;;7211:97;7339:2;7357:53;7402:7;7393:6;7382:9;7378:22;7357:53;;;7347:63;;7318:98;7143:283;;;;;;7433:558;;;7607:2;7595:9;7586:7;7582:23;7578:32;7575:2;;;7623:1;7620;7613:12;7575:2;7658:1;7675:53;7720:7;7711:6;7700:9;7696:22;7675:53;;;7665:63;;7637:97;7793:2;7782:9;7778:18;7765:32;7817:18;7809:6;7806:30;7803:2;;;7849:1;7846;7839:12;7803:2;7869:106;7967:7;7958:6;7947:9;7943:22;7869:106;;;7859:116;;7744:237;7569:422;;;;;;7999:257;;8150:100;8246:3;8238:6;8150:100;;;8136:114;;8129:127;;;;;8265:289;;8408:106;8510:3;8502:6;8408:106;;;8543:4;8538:3;8534:14;8520:28;;8401:153;;;;;8563:261;;8692:92;8780:3;8772:6;8692:92;;;8813:4;8808:3;8804:14;8790:28;;8685:139;;;;;8832:142;8923:45;8962:5;8923:45;;;8918:3;8911:58;8905:69;;;8981:113;9064:24;9082:5;9064:24;;;9059:3;9052:37;9046:48;;;9166:1025;;9370:110;9473:6;9468:3;9370:110;;;9363:117;;9503:3;9545:4;9537:6;9533:17;9528:3;9524:27;9572:84;9650:5;9572:84;;;9676:7;9704:1;9689:463;9714:6;9711:1;9708:13;9689:463;;;9776:9;9770:4;9766:20;9761:3;9754:33;9815:68;9876:6;9867:7;9815:68;;;9898:114;10007:4;9992:13;9898:114;;;9890:122;;10029:88;10110:6;10029:88;;;10019:98;;10140:4;10135:3;10131:14;10124:21;;9746:406;9736:1;9733;9729:9;9724:14;;9689:463;;;9693:14;10165:4;10158:11;;10182:3;10175:10;;9350:841;;;;;;;;;;10290:914;;10491:82;10567:5;10491:82;;;10586:114;10693:6;10688:3;10586:114;;;10579:121;;10721:84;10799:5;10721:84;;;10825:7;10853:1;10838:344;10863:6;10860:1;10857:13;10838:344;;;10930:6;10924:13;10951:119;11066:3;11051:13;10951:119;;;10944:126;;11087:88;11168:6;11087:88;;;11077:98;;10895:287;10885:1;10882;10878:9;10873:14;;10838:344;;;10842:14;11195:3;11188:10;;10470:734;;;;;;;;11269:835;;11465:106;11564:6;11559:3;11465:106;;;11458:113;;11592:80;11666:5;11592:80;;;11692:7;11720:1;11705:377;11730:6;11727:1;11724:13;11705:377;;;11791:64;11848:6;11839:7;11791:64;;;11869:105;11970:3;11955:13;11869:105;;;11862:112;;11991:84;12068:6;11991:84;;;11981:94;;11762:320;11752:1;11749;11745:9;11740:14;;11705:377;;;11709:14;12095:3;12088:10;;11445:659;;;;;;;;12112:104;12189:21;12204:5;12189:21;;;12184:3;12177:34;12171:45;;;12223:103;12296:24;12314:5;12296:24;;;12291:3;12284:37;12278:48;;;12333:113;12416:24;12434:5;12416:24;;;12411:3;12404:37;12398:48;;;12476:273;;12576:60;12629:6;12624:3;12576:60;;;12569:67;;12648:43;12684:6;12679:3;12672:5;12648:43;;;12713:29;12735:6;12713:29;;;12708:3;12704:39;12697:46;;12562:187;;;;;;12757:100;12828:23;12845:5;12828:23;;;12823:3;12816:36;12810:47;;;12864:110;12945:23;12962:5;12945:23;;;12940:3;12933:36;12927:47;;;12982:221;13130:66;13125:3;13118:79;13111:92;;13212:465;;13372:67;13436:2;13431:3;13372:67;;;13365:74;;13472:66;13468:1;13463:3;13459:11;13452:87;13573:66;13568:2;13563:3;13559:12;13552:88;13668:2;13663:3;13659:12;13652:19;;13358:319;;;;13686:465;;13846:67;13910:2;13905:3;13846:67;;;13839:74;;13946:66;13942:1;13937:3;13933:11;13926:87;14047:66;14042:2;14037:3;14033:12;14026:88;14142:2;14137:3;14133:12;14126:19;;13832:319;;;;14160:465;;14320:67;14384:2;14379:3;14320:67;;;14313:74;;14420:66;14416:1;14411:3;14407:11;14400:87;14521:66;14516:2;14511:3;14507:12;14500:88;14616:2;14611:3;14607:12;14600:19;;14306:319;;;;14634:465;;14794:67;14858:2;14853:3;14794:67;;;14787:74;;14894:66;14890:1;14885:3;14881:11;14874:87;14995:66;14990:2;14985:3;14981:12;14974:88;15090:2;15085:3;15081:12;15074:19;;14780:319;;;;15108:465;;15268:67;15332:2;15327:3;15268:67;;;15261:74;;15368:66;15364:1;15359:3;15355:11;15348:87;15469:66;15464:2;15459:3;15455:12;15448:88;15564:2;15559:3;15555:12;15548:19;;15254:319;;;;15582:364;;15742:67;15806:2;15801:3;15742:67;;;15735:74;;15842:66;15838:1;15833:3;15829:11;15822:87;15937:2;15932:3;15928:12;15921:19;;15728:218;;;;16013:1025;;16158:4;16153:3;16149:14;16230:49;16274:3;16267:5;16263:15;16256:5;16230:49;;;16285:62;16342:3;16337;16333:13;16319:12;16285:62;;;16178:175;16409:50;16453:4;16446:5;16442:16;16435:5;16409:50;;;16465:63;16522:4;16517:3;16513:14;16499:12;16465:63;;;16363:171;16590:50;16634:4;16627:5;16623:16;16616:5;16590:50;;;16646:63;16703:4;16698:3;16694:14;16680:12;16646:63;;;16544:171;16794:57;16845:4;16838:5;16834:16;16827:5;16794:57;;;16897:3;16891:4;16887:14;16880:4;16875:3;16871:14;16864:38;16917:83;16995:4;16981:12;16967;16917:83;;;16909:91;;16725:287;;17029:4;17022:11;;16131:907;;;;;;17130:480;17273:4;17268:3;17264:14;17362:3;17355:5;17351:15;17345:22;17373:62;17430:3;17425;17421:13;17407:12;17373:62;;;17293:148;17514:4;17507:5;17503:16;17497:23;17526:63;17583:4;17578:3;17574:14;17560:12;17526:63;;;17451:144;17246:364;;;;17668:877;17797:4;17792:3;17788:14;17869:49;17913:3;17906:5;17902:15;17895:5;17869:49;;;17924:62;17981:3;17976;17972:13;17958:12;17924:62;;;17817:175;18048:50;18092:4;18085:5;18081:16;18074:5;18048:50;;;18104:63;18161:4;18156:3;18152:14;18138:12;18104:63;;;18002:171;18230:49;18273:4;18266:5;18262:16;18255:5;18230:49;;;18285:61;18340:4;18335:3;18331:14;18317:12;18285:61;;;18183:169;18408:49;18451:4;18444:5;18440:16;18433:5;18408:49;;;18463:61;18518:4;18513:3;18509:14;18495:12;18463:61;;;18362:168;17770:775;;;;18615:324;18758:4;18753:3;18749:14;18845:3;18838:5;18834:15;18828:22;18856:62;18913:3;18908;18904:13;18890:12;18856:62;;;18778:146;18731:208;;;;18946:103;19019:24;19037:5;19019:24;;;19014:3;19007:37;19001:48;;;19056:113;19139:24;19157:5;19139:24;;;19134:3;19127:37;19121:48;;;19176:213;;19294:2;19283:9;19279:18;19271:26;;19308:71;19376:1;19365:9;19361:17;19352:6;19308:71;;;19265:124;;;;;19396:340;;19550:2;19539:9;19535:18;19527:26;;19564:79;19640:1;19629:9;19625:17;19616:6;19564:79;;;19654:72;19722:2;19711:9;19707:18;19698:6;19654:72;;;19521:215;;;;;;19743:695;;20023:2;20012:9;20008:18;20000:26;;20037:71;20105:1;20094:9;20090:17;20081:6;20037:71;;;20119:72;20187:2;20176:9;20172:18;20163:6;20119:72;;;20239:9;20233:4;20229:20;20224:2;20213:9;20209:18;20202:48;20264:164;20423:4;20414:6;20264:164;;;20256:172;;19994:444;;;;;;;20445:481;;20673:2;20662:9;20658:18;20650:26;;20723:9;20717:4;20713:20;20709:1;20698:9;20694:17;20687:47;20748:168;20911:4;20902:6;20894;20748:168;;;20740:176;;20644:282;;;;;;20933:465;;21153:2;21142:9;21138:18;21130:26;;21203:9;21197:4;21193:20;21189:1;21178:9;21174:17;21167:47;21228:160;21383:4;21374:6;21366;21228:160;;;21220:168;;21124:274;;;;;;21405:201;;21517:2;21506:9;21502:18;21494:26;;21531:65;21593:1;21582:9;21578:17;21569:6;21531:65;;;21488:118;;;;;21613:435;;21787:2;21776:9;21772:18;21764:26;;21801:71;21869:1;21858:9;21854:17;21845:6;21801:71;;;21883:72;21951:2;21940:9;21936:18;21927:6;21883:72;;;21966;22034:2;22023:9;22019:18;22010:6;21966:72;;;21758:290;;;;;;;22055:539;;22253:3;22242:9;22238:19;22230:27;;22268:71;22336:1;22325:9;22321:17;22312:6;22268:71;;;22350:72;22418:2;22407:9;22403:18;22394:6;22350:72;;;22433:70;22499:2;22488:9;22484:18;22475:6;22433:70;;;22514;22580:2;22569:9;22565:18;22556:6;22514:70;;;22224:370;;;;;;;;22601:339;;22782:2;22771:9;22767:18;22759:26;;22796:134;22927:1;22916:9;22912:17;22796:134;;;22753:187;;;;22947:407;;23138:2;23127:9;23123:18;23115:26;;23188:9;23182:4;23178:20;23174:1;23163:9;23159:17;23152:47;23213:131;23339:4;23213:131;;;23205:139;;23109:245;;;;23361:407;;23552:2;23541:9;23537:18;23529:26;;23602:9;23596:4;23592:20;23588:1;23577:9;23573:17;23566:47;23627:131;23753:4;23627:131;;;23619:139;;23523:245;;;;23775:407;;23966:2;23955:9;23951:18;23943:26;;24016:9;24010:4;24006:20;24002:1;23991:9;23987:17;23980:47;24041:131;24167:4;24041:131;;;24033:139;;23937:245;;;;24189:407;;24380:2;24369:9;24365:18;24357:26;;24430:9;24424:4;24420:20;24416:1;24405:9;24401:17;24394:47;24455:131;24581:4;24455:131;;;24447:139;;24351:245;;;;24603:407;;24794:2;24783:9;24779:18;24771:26;;24844:9;24838:4;24834:20;24830:1;24819:9;24815:17;24808:47;24869:131;24995:4;24869:131;;;24861:139;;24765:245;;;;25017:407;;25208:2;25197:9;25193:18;25185:26;;25258:9;25252:4;25248:20;25244:1;25233:9;25229:17;25222:47;25283:131;25409:4;25283:131;;;25275:139;;25179:245;;;;25431:313;;25599:2;25588:9;25584:18;25576:26;;25613:121;25731:1;25720:9;25716:17;25707:6;25613:121;;;25570:174;;;;;25751:256;;25813:2;25807:9;25797:19;;25851:4;25843:6;25839:17;25950:6;25938:10;25935:22;25914:18;25902:10;25899:34;25896:62;25893:2;;;25971:1;25968;25961:12;25893:2;25991:10;25987:2;25980:22;25791:216;;;;;26014:332;;26201:18;26193:6;26190:30;26187:2;;;26233:1;26230;26223:12;26187:2;26268:4;26260:6;26256:17;26248:25;;26331:4;26325;26321:15;26313:23;;26124:222;;;;26353:144;;26467:3;26459:11;;26453:44;;;;26504:179;;26618:3;26610:11;;26656:4;26651:3;26647:14;26639:22;;26604:79;;;;26690:140;;26800:3;26792:11;;26786:44;;;;26837:165;;26974:5;26968:12;26958:22;;26939:63;;;;27009:136;;27135:4;27130:3;27126:14;27118:22;;27112:33;;;;27152:136;;27278:4;27273:3;27269:14;27261:22;;27255:33;;;;27295:132;;27417:4;27412:3;27408:14;27400:22;;27394:33;;;;27435:202;;27589:6;27584:3;27577:19;27626:4;27621:3;27617:14;27602:29;;27570:67;;;;;27646:206;;27804:6;27799:3;27792:19;27841:4;27836:3;27832:14;27817:29;;27785:67;;;;;27861:198;;28011:6;28006:3;27999:19;28048:4;28043:3;28039:14;28024:29;;27992:67;;;;;28068:152;;28172:6;28167:3;28160:19;28209:4;28204:3;28200:14;28185:29;;28153:67;;;;;28229:163;;28344:6;28339:3;28332:19;28381:4;28376:3;28372:14;28357:29;;28325:67;;;;;28401:119;;28475:39;28510:2;28505:3;28501:12;28496:3;28475:39;;;28466:48;;28459:61;;;;;28529:496;;;28649:3;28636:17;28738:1;28732:4;28728:12;28717:8;28701:14;28697:29;28693:48;28673:18;28669:73;28659:2;;28756:1;28753;28746:12;28659:2;28799:8;28779:18;28775:33;28766:42;;28843:5;28830:19;28820:29;;28875:4;28868:5;28864:16;28855:25;;28900:18;28892:6;28889:30;28886:2;;;28932:1;28929;28922:12;28886:2;28991:3;28983:6;28979:16;28963:14;28959:37;28949:8;28945:52;28942:2;;;29010:1;29007;29000:12;28942:2;28603:422;;;;;;;29034:117;;29107:38;29141:2;29136:3;29132:12;29127:3;29107:38;;;29098:47;;29091:60;;;;;29160:302;;29291:3;29278:17;29380:1;29374:4;29370:12;29359:8;29343:14;29339:29;29335:48;29315:18;29311:73;29301:2;;29398:1;29395;29388:12;29301:2;29441:8;29421:18;29417:33;29408:42;;29245:217;;;;;;29471:105;;29567:3;29558:12;;29551:25;;;;;29585:119;;29659:39;29694:2;29689:3;29685:12;29680:3;29659:39;;;29650:48;;29643:61;;;;;29712:91;;29774:24;29792:5;29774:24;;;29763:35;;29757:46;;;;29810:85;;29883:5;29876:13;29869:21;29858:32;;29852:43;;;;29902:72;;29964:5;29953:16;;29947:27;;;;29981:71;;30042:5;30031:16;;30025:27;;;;30059:121;;30132:42;30125:5;30121:54;30110:65;;30104:76;;;;30187:72;;30249:5;30238:16;;30232:27;;;;30266:129;;30353:37;30384:5;30353:37;;;30340:50;;30334:61;;;;30402:121;;30481:37;30512:5;30481:37;;;30468:50;;30462:61;;;;30530:108;;30609:24;30627:5;30609:24;;;30596:37;;30590:48;;;;30646:145;30727:6;30722:3;30717;30704:30;30783:1;30774:6;30769:3;30765:16;30758:27;30697:94;;;;30799:97;;30887:2;30883:7;30878:2;30871:5;30867:14;30863:28;30853:38;;30847:49;;;;30904:117;30973:24;30991:5;30973:24;;;30966:5;30963:35;30953:2;;31012:1;31009;31002:12;30953:2;30947:74;;31028:111;31094:21;31109:5;31094:21;;;31087:5;31084:32;31074:2;;31130:1;31127;31120:12;31074:2;31068:71;;31146:117;31215:24;31233:5;31215:24;;;31208:5;31205:35;31195:2;;31254:1;31251;31244:12;31195:2;31189:74;;31270:115;31338:23;31355:5;31338:23;;;31331:5;31328:34;31318:2;;31376:1;31373;31366:12;31318:2;31312:73;;31392:117;31461:24;31479:5;31461:24;;;31454:5;31451:35;31441:2;;31500:1;31497;31490:12;31441:2;31435:74;;10591:146:0;10687:42;10699:14;10715:13;10687:11;:42::i;:::-;10591:146;;:::o

Swarm Source

bzzr://8db37f5a9ca4dec4c48dbab8e8abe86a3860059359d379938d27180183394343

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.