GLMR Price: $0.07761 (+10.21%)

Contract

0x3Abc736a9B980aBD3C4a2314f731b08C2341135D

Overview

GLMR Balance

Moonbeam Chain LogoMoonbeam Chain LogoMoonbeam Chain Logo0 GLMR

GLMR Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim Achievemen...48042172023-11-05 17:50:42534 days ago1699206642IN
0x3Abc736a...C2341135D
0 GLMR0.02916996127.5
Claim Achievemen...37154372023-06-05 12:40:18687 days ago1685968818IN
0x3Abc736a...C2341135D
0 GLMR0.03169954140.19400419
Claim Achievemen...28303062023-01-28 23:34:36815 days ago1674948876IN
0x3Abc736a...C2341135D
0 GLMR0.02932432102.5
Claim Achievemen...26134792022-12-29 9:15:24845 days ago1672305324IN
0x3Abc736a...C2341135D
0 GLMR0.03713841102.5
Claim Achievemen...25935282022-12-26 13:33:06848 days ago1672061586IN
0x3Abc736a...C2341135D
0 GLMR0.02707991101.41
Claim Achievemen...25935232022-12-26 13:32:06848 days ago1672061526IN
0x3Abc736a...C2341135D
0 GLMR0.02295036101.5
Claim Achievemen...25926502022-12-26 10:34:42848 days ago1672050882IN
0x3Abc736a...C2341135D
0 GLMR0.04104592102.5
Claim Achievemen...25901032022-12-26 1:58:00849 days ago1672019880IN
0x3Abc736a...C2341135D
0 GLMR0.03713841102.5
Claim Achievemen...25548322022-12-21 2:37:30854 days ago1671590250IN
0x3Abc736a...C2341135D
0 GLMR0.02317648102.5
Claim Achievemen...25517412022-12-20 16:07:36854 days ago1671552456IN
0x3Abc736a...C2341135D
0 GLMR0.03713841102.5
Claim Achievemen...25495182022-12-20 8:37:12854 days ago1671525432IN
0x3Abc736a...C2341135D
0 GLMR0.02932432102.5
Claim Achievemen...25495162022-12-20 8:36:48854 days ago1671525408IN
0x3Abc736a...C2341135D
0 GLMR0.02737098102.5
Claim Achievemen...25495142022-12-20 8:36:24854 days ago1671525384IN
0x3Abc736a...C2341135D
0 GLMR0.02317648102.5
Claim Achievemen...25453682022-12-19 18:40:06855 days ago1671475206IN
0x3Abc736a...C2341135D
0 GLMR0.05920762135
Claim Achievemen...25444192022-12-19 15:25:36855 days ago1671463536IN
0x3Abc736a...C2341135D
0 GLMR0.04495393102.5
Claim Achievemen...25444162022-12-19 15:25:00855 days ago1671463500IN
0x3Abc736a...C2341135D
0 GLMR0.05136459102.5
Claim Achievemen...25428052022-12-19 9:58:24855 days ago1671443904IN
0x3Abc736a...C2341135D
0 GLMR0.0708768102.5
Claim Achievemen...25427992022-12-19 9:57:12855 days ago1671443832IN
0x3Abc736a...C2341135D
0 GLMR0.05277089102.5
Claim Achievemen...25427762022-12-19 9:52:36855 days ago1671443556IN
0x3Abc736a...C2341135D
0 GLMR0.02932432102.5
Claim Achievemen...25427762022-12-19 9:52:36855 days ago1671443556IN
0x3Abc736a...C2341135D
0 GLMR0.042964102.5
Claim Achievemen...25406882022-12-19 2:50:48856 days ago1671418248IN
0x3Abc736a...C2341135D
0 GLMR0.05277089102.5
Claim Achievemen...25364282022-12-18 12:28:00856 days ago1671366480IN
0x3Abc736a...C2341135D
0 GLMR0.03323111102.5
Claim Achievemen...25337772022-12-18 3:29:24857 days ago1671334164IN
0x3Abc736a...C2341135D
0 GLMR0.03713841102.5
Claim Achievemen...25337732022-12-18 3:28:30857 days ago1671334110IN
0x3Abc736a...C2341135D
0 GLMR0.04159398102.5
Claim Achievemen...25320322022-12-17 21:37:36857 days ago1671313056IN
0x3Abc736a...C2341135D
0 GLMR0.04495393102.5
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Achievements

Compiler Version
v0.6.2+commit.bacdbe57

Optimization Enabled:
Yes with 1 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at moonbeam.moonscan.io on 2022-11-21
*/

// File: contracts/RealitioERC20.sol

/**
 *Submitted for verification at Etherscan.io on 2021-06-09
*/

pragma solidity >0.4.24;

/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
interface IERC20R {
    function totalSupply() external view returns (uint256);

    function balanceOf(address who) external view returns (uint256);

    function allowance(address owner, address spender) external view returns (uint256);

    function transfer(address to, uint256 value) external returns (bool);

    function approve(address spender, uint256 value) external returns (bool);

    function transferFrom(address from, address to, uint256 value) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(address indexed owner, address indexed spender, uint256 value);
}
pragma solidity >0.4.24;

/**
 * @title ReailtioSafeMath256
 * @dev Math operations with safety checks that throw on error
 */
library RealitioSafeMath256 {
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}
pragma solidity >0.4.24;

/**
 * @title RealitioSafeMath32
 * @dev Math operations with safety checks that throw on error
 * @dev Copy of SafeMath but for uint32 instead of uint256
 * @dev Deleted functions we don't use
 */
library RealitioSafeMath32 {
  function add(uint32 a, uint32 b) internal pure returns (uint32) {
    uint32 c = a + b;
    assert(c >= a);
    return c;
  }
}
pragma solidity >0.4.18;


contract BalanceHolder {

    IERC20R public token;

    mapping(address => uint256) public balanceOf;

    event LogWithdraw(
        address indexed user,
        uint256 amount
    );

    function withdraw()
    public {
        uint256 bal = balanceOf[msg.sender];
        balanceOf[msg.sender] = 0;
        require(token.transfer(msg.sender, bal));
        emit LogWithdraw(msg.sender, bal);
    }

}
pragma solidity >0.4.24;


contract RealitioERC20 is BalanceHolder {

    using RealitioSafeMath256 for uint256;
    using RealitioSafeMath32 for uint32;

    address constant NULL_ADDRESS = address(0);

    // History hash when no history is created, or history has been cleared
    bytes32 constant NULL_HASH = bytes32(0);

    // An unitinalized finalize_ts for a question will indicate an unanswered question.
    uint32 constant UNANSWERED = 0;

    // An unanswered reveal_ts for a commitment will indicate that it does not exist.
    uint256 constant COMMITMENT_NON_EXISTENT = 0;

    // Commit->reveal timeout is 1/8 of the question timeout (rounded down).
    uint32 constant COMMITMENT_TIMEOUT_RATIO = 8;

    event LogSetQuestionFee(
        address arbitrator,
        uint256 amount
    );

    event LogNewTemplate(
        uint256 indexed template_id,
        address indexed user,
        string question_text
    );

    event LogNewQuestion(
        bytes32 indexed question_id,
        address indexed user,
        uint256 template_id,
        string question,
        bytes32 indexed content_hash,
        address arbitrator,
        uint32 timeout,
        uint32 opening_ts,
        uint256 nonce,
        uint256 created
    );

    event LogFundAnswerBounty(
        bytes32 indexed question_id,
        uint256 bounty_added,
        uint256 bounty,
        address indexed user
    );

    event LogNewAnswer(
        bytes32 answer,
        bytes32 indexed question_id,
        bytes32 history_hash,
        address indexed user,
        uint256 bond,
        uint256 ts,
        bool is_commitment
    );

    event LogAnswerReveal(
        bytes32 indexed question_id,
        address indexed user,
        bytes32 indexed answer_hash,
        bytes32 answer,
        uint256 nonce,
        uint256 bond
    );

    event LogNotifyOfArbitrationRequest(
        bytes32 indexed question_id,
        address indexed user
    );

    event LogFinalize(
        bytes32 indexed question_id,
        bytes32 indexed answer
    );

    event LogClaim(
        bytes32 indexed question_id,
        address indexed user,
        uint256 amount
    );

    struct Question {
        bytes32 content_hash;
        address arbitrator;
        uint32 opening_ts;
        uint32 timeout;
        uint32 finalize_ts;
        bool is_pending_arbitration;
        uint256 bounty;
        bytes32 best_answer;
        bytes32 history_hash;
        uint256 bond;
    }

    // Stored in a mapping indexed by commitment_id, a hash of commitment hash, question, bond.
    struct Commitment {
        uint32 reveal_ts;
        bool is_revealed;
        bytes32 revealed_answer;
    }

    // Only used when claiming more bonds than fits into a transaction
    // Stored in a mapping indexed by question_id.
    struct Claim {
        address payee;
        uint256 last_bond;
        uint256 queued_funds;
    }

    uint256 nextTemplateID = 0;
    mapping(uint256 => uint256) public templates;
    mapping(uint256 => bytes32) public template_hashes;
    mapping(bytes32 => Question) public questions;
    mapping(bytes32 => Claim) public question_claims;
    mapping(bytes32 => Commitment) public commitments;
    mapping(address => uint256) public arbitrator_question_fees;

    modifier onlyArbitrator(bytes32 question_id) {
        require(msg.sender == questions[question_id].arbitrator, "msg.sender must be arbitrator");
        _;
    }

    modifier stateAny() {
        _;
    }

    modifier stateNotCreated(bytes32 question_id) {
        require(questions[question_id].timeout == 0, "question must not exist");
        _;
    }

    modifier stateOpen(bytes32 question_id) {
        require(questions[question_id].timeout > 0, "question must exist");
        require(!questions[question_id].is_pending_arbitration, "question must not be pending arbitration");
        uint32 finalize_ts = questions[question_id].finalize_ts;
        require(finalize_ts == UNANSWERED || finalize_ts > uint32(now), "finalization deadline must not have passed");
        uint32 opening_ts = questions[question_id].opening_ts;
        require(opening_ts == 0 || opening_ts <= uint32(now), "opening date must have passed");
        _;
    }

    modifier statePendingArbitration(bytes32 question_id) {
        require(questions[question_id].is_pending_arbitration, "question must be pending arbitration");
        _;
    }

    modifier stateOpenOrPendingArbitration(bytes32 question_id) {
        require(questions[question_id].timeout > 0, "question must exist");
        uint32 finalize_ts = questions[question_id].finalize_ts;
        require(finalize_ts == UNANSWERED || finalize_ts > uint32(now), "finalization dealine must not have passed");
        uint32 opening_ts = questions[question_id].opening_ts;
        require(opening_ts == 0 || opening_ts <= uint32(now), "opening date must have passed");
        _;
    }

    modifier stateFinalized(bytes32 question_id) {
        require(isFinalized(question_id), "question must be finalized");
        _;
    }

    modifier bondMustDouble(bytes32 question_id, uint256 tokens) {
        require(tokens > 0, "bond must be positive");
        require(tokens >= (questions[question_id].bond.mul(2)), "bond must be double at least previous bond");
        _;
    }

    modifier previousBondMustNotBeatMaxPrevious(bytes32 question_id, uint256 max_previous) {
        if (max_previous > 0) {
            require(questions[question_id].bond <= max_previous, "bond must exceed max_previous");
        }
        _;
    }

    function setToken(IERC20R _token)
    public
    {
        require(token == IERC20R(0x0), "Token can only be initialized once");
        token = _token;
    }

    /// @notice Constructor, sets up some initial templates
    /// @dev Creates some generalized templates for different question types used in the DApp.
    constructor()
    public {
        createTemplate('{"title": "%s", "type": "bool", "category": "%s", "lang": "%s"}');
        createTemplate('{"title": "%s", "type": "uint", "decimals": 18, "category": "%s", "lang": "%s"}');
        createTemplate('{"title": "%s", "type": "single-select", "outcomes": [%s], "category": "%s", "lang": "%s"}');
        createTemplate('{"title": "%s", "type": "multiple-select", "outcomes": [%s], "category": "%s", "lang": "%s"}');
        createTemplate('{"title": "%s", "type": "datetime", "category": "%s", "lang": "%s"}');
    }

    /// @notice Function for arbitrator to set an optional per-question fee.
    /// @dev The per-question fee, charged when a question is asked, is intended as an anti-spam measure.
    /// @param fee The fee to be charged by the arbitrator when a question is asked
    function setQuestionFee(uint256 fee)
        stateAny()
    external {
        arbitrator_question_fees[msg.sender] = fee;
        emit LogSetQuestionFee(msg.sender, fee);
    }

    /// @notice Create a reusable template, which should be a JSON document.
    /// Placeholders should use gettext() syntax, eg %s.
    /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
    /// @param content The template content
    /// @return The ID of the newly-created template, which is created sequentially.
    function createTemplate(string memory content)
        stateAny()
    public returns (uint256) {
        uint256 id = nextTemplateID;
        templates[id] = block.number;
        template_hashes[id] = keccak256(abi.encodePacked(content));
        emit LogNewTemplate(id, msg.sender, content);
        nextTemplateID = id.add(1);
        return id;
    }

    /// @notice Create a new reusable template and use it to ask a question
    /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
    /// @param content The template content
    /// @param question A string containing the parameters that will be passed into the template to make the question
    /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
    /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
    /// @param opening_ts If set, the earliest time it should be possible to answer the question.
    /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
    /// @return The ID of the newly-created template, which is created sequentially.
    function createTemplateAndAskQuestion(
        string memory content,
        string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce
    )
        // stateNotCreated is enforced by the internal _askQuestion
    public returns (bytes32) {
        uint256 template_id = createTemplate(content);
        return askQuestion(template_id, question, arbitrator, timeout, opening_ts, nonce);
    }

    /// @notice Ask a new question without a bounty and return the ID
    /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
    /// @dev Calling without the token param will only work if there is no arbitrator-set question fee.
    /// @dev This has the same function signature as askQuestion() in the non-ERC20 version, which is optionally payable.
    /// @param template_id The ID number of the template the question will use
    /// @param question A string containing the parameters that will be passed into the template to make the question
    /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
    /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
    /// @param opening_ts If set, the earliest time it should be possible to answer the question.
    /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
    /// @return The ID of the newly-created question, created deterministically.
    function askQuestion(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce)
        // stateNotCreated is enforced by the internal _askQuestion
    public returns (bytes32) {

        require(templates[template_id] > 0, "template must exist");

        bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
        bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, msg.sender, nonce));

        _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, 0);
        emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, now);

        return question_id;
    }

    /// @notice Ask a new question with a bounty and return the ID
    /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage.
    /// @param template_id The ID number of the template the question will use
    /// @param question A string containing the parameters that will be passed into the template to make the question
    /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
    /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
    /// @param opening_ts If set, the earliest time it should be possible to answer the question.
    /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
    /// @param tokens The combined initial question bounty and question fee
    /// @return The ID of the newly-created question, created deterministically.
    function askQuestionERC20(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 tokens)
        // stateNotCreated is enforced by the internal _askQuestion
    public returns (bytes32) {

        _deductTokensOrRevert(tokens);

        require(templates[template_id] > 0, "template must exist");

        bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
        bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, msg.sender, nonce));

        _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, tokens);
        emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, now);

        return question_id;
    }

    function _deductTokensOrRevert(uint256 tokens)
    internal {

        if (tokens == 0) {
            return;
        }

        uint256 bal = balanceOf[msg.sender];

        // Deduct any tokens you have in your internal balance first
        if (bal > 0) {
            if (bal >= tokens) {
                balanceOf[msg.sender] = bal.sub(tokens);
                return;
            } else {
                tokens = tokens.sub(bal);
                balanceOf[msg.sender] = 0;
            }
        }
        // Now we need to charge the rest from
        require(token.transferFrom(msg.sender, address(this), tokens), "Transfer of tokens failed, insufficient approved balance?");
        return;

    }

    function _askQuestion(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 tokens)
        stateNotCreated(question_id)
    internal {

        uint256 bounty = tokens;

        // A timeout of 0 makes no sense, and we will use this to check existence
        require(timeout > 0, "timeout must be positive");
        require(timeout < 365 days, "timeout must be less than 365 days");
        require(arbitrator != NULL_ADDRESS, "arbitrator must be set");

        // The arbitrator can set a fee for asking a question.
        // This is intended as an anti-spam defence.
        // The fee is waived if the arbitrator is asking the question.
        // This allows them to set an impossibly high fee and make users proxy the question through them.
        // This would allow more sophisticated pricing, question whitelisting etc.
        if (msg.sender != arbitrator) {
            uint256 question_fee = arbitrator_question_fees[arbitrator];
            require(bounty >= question_fee, "Tokens provided must cover question fee");
            bounty = bounty.sub(question_fee);
            balanceOf[arbitrator] = balanceOf[arbitrator].add(question_fee);
        }

        questions[question_id].content_hash = content_hash;
        questions[question_id].arbitrator = arbitrator;
        questions[question_id].opening_ts = opening_ts;
        questions[question_id].timeout = timeout;
        questions[question_id].bounty = bounty;

    }

    /// @notice Add funds to the bounty for a question
    /// @dev Add bounty funds after the initial question creation. Can be done any time until the question is finalized.
    /// @param question_id The ID of the question you wish to fund
    /// @param tokens The number of tokens to fund
    function fundAnswerBountyERC20(bytes32 question_id, uint256 tokens)
        stateOpen(question_id)
    external {
        _deductTokensOrRevert(tokens);
        questions[question_id].bounty = questions[question_id].bounty.add(tokens);
        emit LogFundAnswerBounty(question_id, tokens, questions[question_id].bounty, msg.sender);
    }

    /// @notice Submit an answer for a question.
    /// @dev Adds the answer to the history and updates the current "best" answer.
    /// May be subject to front-running attacks; Substitute submitAnswerCommitment()->submitAnswerReveal() to prevent them.
    /// @param question_id The ID of the question
    /// @param answer The answer, encoded into bytes32
    /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
    /// @param tokens The amount of tokens to submit
    function submitAnswerERC20(bytes32 question_id, bytes32 answer, uint256 max_previous, uint256 tokens)
        stateOpen(question_id)
        bondMustDouble(question_id, tokens)
        previousBondMustNotBeatMaxPrevious(question_id, max_previous)
    external {
        _deductTokensOrRevert(tokens);
        _addAnswerToHistory(question_id, answer, msg.sender, tokens, false);
        _updateCurrentAnswer(question_id, answer, questions[question_id].timeout);
    }

    // @notice Verify and store a commitment, including an appropriate timeout
    // @param question_id The ID of the question to store
    // @param commitment The ID of the commitment
    function _storeCommitment(bytes32 question_id, bytes32 commitment_id)
    internal
    {
        require(commitments[commitment_id].reveal_ts == COMMITMENT_NON_EXISTENT, "commitment must not already exist");

        uint32 commitment_timeout = questions[question_id].timeout / COMMITMENT_TIMEOUT_RATIO;
        commitments[commitment_id].reveal_ts = uint32(now).add(commitment_timeout);
    }

    /// @notice Submit the hash of an answer, laying your claim to that answer if you reveal it in a subsequent transaction.
    /// @dev Creates a hash, commitment_id, uniquely identifying this answer, to this question, with this bond.
    /// The commitment_id is stored in the answer history where the answer would normally go.
    /// Does not update the current best answer - this is left to the later submitAnswerReveal() transaction.
    /// @param question_id The ID of the question
    /// @param answer_hash The hash of your answer, plus a nonce that you will later reveal
    /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
    /// @param _answerer If specified, the address to be given as the question answerer. Defaults to the sender.
    /// @param tokens Number of tokens sent
    /// @dev Specifying the answerer is useful if you want to delegate the commit-and-reveal to a third-party.
    function submitAnswerCommitmentERC20(bytes32 question_id, bytes32 answer_hash, uint256 max_previous, address _answerer, uint256 tokens)
        stateOpen(question_id)
        bondMustDouble(question_id, tokens)
        previousBondMustNotBeatMaxPrevious(question_id, max_previous)
    external {

        _deductTokensOrRevert(tokens);

        bytes32 commitment_id = keccak256(abi.encodePacked(question_id, answer_hash, tokens));
        address answerer = (_answerer == NULL_ADDRESS) ? msg.sender : _answerer;

        _storeCommitment(question_id, commitment_id);
        _addAnswerToHistory(question_id, commitment_id, answerer, tokens, true);

    }

    /// @notice Submit the answer whose hash you sent in a previous submitAnswerCommitment() transaction
    /// @dev Checks the parameters supplied recreate an existing commitment, and stores the revealed answer
    /// Updates the current answer unless someone has since supplied a new answer with a higher bond
    /// msg.sender is intentionally not restricted to the user who originally sent the commitment;
    /// For example, the user may want to provide the answer+nonce to a third-party service and let them send the tx
    /// NB If we are pending arbitration, it will be up to the arbitrator to wait and see any outstanding reveal is sent
    /// @param question_id The ID of the question
    /// @param answer The answer, encoded as bytes32
    /// @param nonce The nonce that, combined with the answer, recreates the answer_hash you gave in submitAnswerCommitment()
    /// @param bond The bond that you paid in your submitAnswerCommitment() transaction
    function submitAnswerReveal(bytes32 question_id, bytes32 answer, uint256 nonce, uint256 bond)
        stateOpenOrPendingArbitration(question_id)
    external {

        bytes32 answer_hash = keccak256(abi.encodePacked(answer, nonce));
        bytes32 commitment_id = keccak256(abi.encodePacked(question_id, answer_hash, bond));

        require(!commitments[commitment_id].is_revealed, "commitment must not have been revealed yet");
        require(commitments[commitment_id].reveal_ts > uint32(now), "reveal deadline must not have passed");

        commitments[commitment_id].revealed_answer = answer;
        commitments[commitment_id].is_revealed = true;

        if (bond == questions[question_id].bond) {
            _updateCurrentAnswer(question_id, answer, questions[question_id].timeout);
        }

        emit LogAnswerReveal(question_id, msg.sender, answer_hash, answer, nonce, bond);

    }

    function _addAnswerToHistory(bytes32 question_id, bytes32 answer_or_commitment_id, address answerer, uint256 bond, bool is_commitment)
    internal
    {
        bytes32 new_history_hash = keccak256(abi.encodePacked(questions[question_id].history_hash, answer_or_commitment_id, bond, answerer, is_commitment));

        // Update the current bond level, if there's a bond (ie anything except arbitration)
        if (bond > 0) {
            questions[question_id].bond = bond;
        }
        questions[question_id].history_hash = new_history_hash;

        emit LogNewAnswer(answer_or_commitment_id, question_id, new_history_hash, answerer, bond, now, is_commitment);
    }

    function _updateCurrentAnswer(bytes32 question_id, bytes32 answer, uint32 timeout_secs)
    internal {
        questions[question_id].best_answer = answer;
        questions[question_id].finalize_ts = uint32(now).add(timeout_secs);
    }

    /// @notice Notify the contract that the arbitrator has been paid for a question, freezing it pending their decision.
    /// @dev The arbitrator contract is trusted to only call this if they've been paid, and tell us who paid them.
    /// @param question_id The ID of the question
    /// @param requester The account that requested arbitration
    /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction.
    function notifyOfArbitrationRequest(bytes32 question_id, address requester, uint256 max_previous)
        onlyArbitrator(question_id)
        stateOpen(question_id)
        previousBondMustNotBeatMaxPrevious(question_id, max_previous)
    external {
        require(questions[question_id].bond > 0, "Question must already have an answer when arbitration is requested");
        questions[question_id].is_pending_arbitration = true;
        emit LogNotifyOfArbitrationRequest(question_id, requester);
    }

    /// @notice Submit the answer for a question, for use by the arbitrator.
    /// @dev Doesn't require (or allow) a bond.
    /// If the current final answer is correct, the account should be whoever submitted it.
    /// If the current final answer is wrong, the account should be whoever paid for arbitration.
    /// However, the answerer stipulations are not enforced by the contract.
    /// @param question_id The ID of the question
    /// @param answer The answer, encoded into bytes32
    /// @param answerer The account credited with this answer for the purpose of bond claims
    function submitAnswerByArbitrator(bytes32 question_id, bytes32 answer, address answerer)
        onlyArbitrator(question_id)
        statePendingArbitration(question_id)
    external {

        require(answerer != NULL_ADDRESS, "answerer must be provided");
        emit LogFinalize(question_id, answer);

        questions[question_id].is_pending_arbitration = false;
        _addAnswerToHistory(question_id, answer, answerer, 0, false);
        _updateCurrentAnswer(question_id, answer, 0);

    }

    /// @notice Report whether the answer to the specified question is finalized
    /// @param question_id The ID of the question
    /// @return Return true if finalized
    function isFinalized(bytes32 question_id)
    view public returns (bool) {
        uint32 finalize_ts = questions[question_id].finalize_ts;
        return ( !questions[question_id].is_pending_arbitration && (finalize_ts > UNANSWERED) && (finalize_ts <= uint32(now)) );
    }

    /// @notice (Deprecated) Return the final answer to the specified question, or revert if there isn't one
    /// @param question_id The ID of the question
    /// @return The answer formatted as a bytes32
    function getFinalAnswer(bytes32 question_id)
        stateFinalized(question_id)
    external view returns (bytes32) {
        return questions[question_id].best_answer;
    }

    /// @notice Return the final answer to the specified question, or revert if there isn't one
    /// @param question_id The ID of the question
    /// @return The answer formatted as a bytes32
    function resultFor(bytes32 question_id)
        stateFinalized(question_id)
    external view returns (bytes32) {
        return questions[question_id].best_answer;
    }


    /// @notice Return the final answer to the specified question, provided it matches the specified criteria.
    /// @dev Reverts if the question is not finalized, or if it does not match the specified criteria.
    /// @param question_id The ID of the question
    /// @param content_hash The hash of the question content (template ID + opening time + question parameter string)
    /// @param arbitrator The arbitrator chosen for the question (regardless of whether they are asked to arbitrate)
    /// @param min_timeout The timeout set in the initial question settings must be this high or higher
    /// @param min_bond The bond sent with the final answer must be this high or higher
    /// @return The answer formatted as a bytes32
    function getFinalAnswerIfMatches(
        bytes32 question_id,
        bytes32 content_hash, address arbitrator, uint32 min_timeout, uint256 min_bond
    )
        stateFinalized(question_id)
    external view returns (bytes32) {
        require(content_hash == questions[question_id].content_hash, "content hash must match");
        require(arbitrator == questions[question_id].arbitrator, "arbitrator must match");
        require(min_timeout <= questions[question_id].timeout, "timeout must be long enough");
        require(min_bond <= questions[question_id].bond, "bond must be high enough");
        return questions[question_id].best_answer;
    }

    /// @notice Assigns the winnings (bounty and bonds) to everyone who gave the accepted answer
    /// Caller must provide the answer history, in reverse order
    /// @dev Works up the chain and assign bonds to the person who gave the right answer
    /// If someone gave the winning answer earlier, they must get paid from the higher bond
    /// That means we can't pay out the bond added at n until we have looked at n-1
    /// The first answer is authenticated by checking against the stored history_hash.
    /// One of the inputs to history_hash is the history_hash before it, so we use that to authenticate the next entry, etc
    /// Once we get to a null hash we'll know we're done and there are no more answers.
    /// Usually you would call the whole thing in a single transaction, but if not then the data is persisted to pick up later.
    /// @param question_id The ID of the question
    /// @param history_hashes Second-last-to-first, the hash of each history entry. (Final one should be empty).
    /// @param addrs Last-to-first, the address of each answerer or commitment sender
    /// @param bonds Last-to-first, the bond supplied with each answer or commitment
    /// @param answers Last-to-first, each answer supplied, or commitment ID if the answer was supplied with commit->reveal
    function claimWinnings(
        bytes32 question_id,
        bytes32[] memory history_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers
    )
        stateFinalized(question_id)
    public {

        require(history_hashes.length > 0, "at least one history hash entry must be provided");

        // These are only set if we split our claim over multiple transactions.
        address payee = question_claims[question_id].payee;
        uint256 last_bond = question_claims[question_id].last_bond;
        uint256 queued_funds = question_claims[question_id].queued_funds;

        // Starts as the hash of the final answer submitted. It'll be cleared when we're done.
        // If we're splitting the claim over multiple transactions, it'll be the hash where we left off last time
        bytes32 last_history_hash = questions[question_id].history_hash;

        bytes32 best_answer = questions[question_id].best_answer;

        uint256 i;
        for (i = 0; i < history_hashes.length; i++) {

            // Check input against the history hash, and see which of 2 possible values of is_commitment fits.
            bool is_commitment = _verifyHistoryInputOrRevert(last_history_hash, history_hashes[i], answers[i], bonds[i], addrs[i]);

            queued_funds = queued_funds.add(last_bond);
            (queued_funds, payee) = _processHistoryItem(
                question_id, best_answer, queued_funds, payee,
                addrs[i], bonds[i], answers[i], is_commitment);

            // Line the bond up for next time, when it will be added to somebody's queued_funds
            last_bond = bonds[i];
            last_history_hash = history_hashes[i];

        }

        if (last_history_hash != NULL_HASH) {
            // We haven't yet got to the null hash (1st answer), ie the caller didn't supply the full answer chain.
            // Persist the details so we can pick up later where we left off later.

            // If we know who to pay we can go ahead and pay them out, only keeping back last_bond
            // (We always know who to pay unless all we saw were unrevealed commits)
            if (payee != NULL_ADDRESS) {
                _payPayee(question_id, payee, queued_funds);
                queued_funds = 0;
            }

            question_claims[question_id].payee = payee;
            question_claims[question_id].last_bond = last_bond;
            question_claims[question_id].queued_funds = queued_funds;
        } else {
            // There is nothing left below us so the payee can keep what remains
            _payPayee(question_id, payee, queued_funds.add(last_bond));
            delete question_claims[question_id];
        }

        questions[question_id].history_hash = last_history_hash;

    }

    function _payPayee(bytes32 question_id, address payee, uint256 value)
    internal {
        balanceOf[payee] = balanceOf[payee].add(value);
        emit LogClaim(question_id, payee, value);
    }

    function _verifyHistoryInputOrRevert(
        bytes32 last_history_hash,
        bytes32 history_hash, bytes32 answer, uint256 bond, address addr
    )
    internal pure returns (bool) {
        if (last_history_hash == keccak256(abi.encodePacked(history_hash, answer, bond, addr, true)) ) {
            return true;
        }
        if (last_history_hash == keccak256(abi.encodePacked(history_hash, answer, bond, addr, false)) ) {
            return false;
        }
        revert("History input provided did not match the expected hash");
    }

    function _processHistoryItem(
        bytes32 question_id, bytes32 best_answer,
        uint256 queued_funds, address payee,
        address addr, uint256 bond, bytes32 answer, bool is_commitment
    )
    internal returns (uint256, address) {

        // For commit-and-reveal, the answer history holds the commitment ID instead of the answer.
        // We look at the referenced commitment ID and switch in the actual answer.
        if (is_commitment) {
            bytes32 commitment_id = answer;
            // If it's a commit but it hasn't been revealed, it will always be considered wrong.
            if (!commitments[commitment_id].is_revealed) {
                delete commitments[commitment_id];
                return (queued_funds, payee);
            } else {
                answer = commitments[commitment_id].revealed_answer;
                delete commitments[commitment_id];
            }
        }

        if (answer == best_answer) {

            if (payee == NULL_ADDRESS) {

                // The entry is for the first payee we come to, ie the winner.
                // They get the question bounty.
                payee = addr;
                queued_funds = queued_funds.add(questions[question_id].bounty);
                questions[question_id].bounty = 0;

            } else if (addr != payee) {

                // Answerer has changed, ie we found someone lower down who needs to be paid

                // The lower answerer will take over receiving bonds from higher answerer.
                // They should also be paid the takeover fee, which is set at a rate equivalent to their bond.
                // (This is our arbitrary rule, to give consistent right-answerers a defence against high-rollers.)

                // There should be enough for the fee, but if not, take what we have.
                // There's an edge case involving weird arbitrator behaviour where we may be short.
                uint256 answer_takeover_fee = (queued_funds >= bond) ? bond : queued_funds;

                // Settle up with the old (higher-bonded) payee
                _payPayee(question_id, payee, queued_funds.sub(answer_takeover_fee));

                // Now start queued_funds again for the new (lower-bonded) payee
                payee = addr;
                queued_funds = answer_takeover_fee;

            }

        }

        return (queued_funds, payee);

    }

    /// @notice Convenience function to assign bounties/bonds for multiple questions in one go, then withdraw all your funds.
    /// Caller must provide the answer history for each question, in reverse order
    /// @dev Can be called by anyone to assign bonds/bounties, but funds are only withdrawn for the user making the call.
    /// @param question_ids The IDs of the questions you want to claim for
    /// @param lengths The number of history entries you will supply for each question ID
    /// @param hist_hashes In a single list for all supplied questions, the hash of each history entry.
    /// @param addrs In a single list for all supplied questions, the address of each answerer or commitment sender
    /// @param bonds In a single list for all supplied questions, the bond supplied with each answer or commitment
    /// @param answers In a single list for all supplied questions, each answer supplied, or commitment ID
    function claimMultipleAndWithdrawBalance(
        bytes32[] memory question_ids, uint256[] memory lengths,
        bytes32[] memory hist_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers
    )
        stateAny() // The finalization checks are done in the claimWinnings function
    public {

        uint256 qi;
        uint256 i;
        for (qi = 0; qi < question_ids.length; qi++) {
            bytes32 qid = question_ids[qi];
            uint256 ln = lengths[qi];
            bytes32[] memory hh = new bytes32[](ln);
            address[] memory ad = new address[](ln);
            uint256[] memory bo = new uint256[](ln);
            bytes32[] memory an = new bytes32[](ln);
            uint256 j;
            for (j = 0; j < ln; j++) {
                hh[j] = hist_hashes[i];
                ad[j] = addrs[i];
                bo[j] = bonds[i];
                an[j] = answers[i];
                i++;
            }
            claimWinnings(qid, hh, ad, bo, an);
        }
        withdraw();
    }

    /// @notice Returns the questions's content hash, identifying the question content
    /// @param question_id The ID of the question
    function getContentHash(bytes32 question_id)
    public view returns(bytes32) {
        return questions[question_id].content_hash;
    }

    /// @notice Returns the arbitrator address for the question
    /// @param question_id The ID of the question
    function getArbitrator(bytes32 question_id)
    public view returns(address) {
        return questions[question_id].arbitrator;
    }

    /// @notice Returns the timestamp when the question can first be answered
    /// @param question_id The ID of the question
    function getOpeningTS(bytes32 question_id)
    public view returns(uint32) {
        return questions[question_id].opening_ts;
    }

    /// @notice Returns the timeout in seconds used after each answer
    /// @param question_id The ID of the question
    function getTimeout(bytes32 question_id)
    public view returns(uint32) {
        return questions[question_id].timeout;
    }

    /// @notice Returns the timestamp at which the question will be/was finalized
    /// @param question_id The ID of the question
    function getFinalizeTS(bytes32 question_id)
    public view returns(uint32) {
        return questions[question_id].finalize_ts;
    }

    /// @notice Returns whether the question is pending arbitration
    /// @param question_id The ID of the question
    function isPendingArbitration(bytes32 question_id)
    public view returns(bool) {
        return questions[question_id].is_pending_arbitration;
    }

    /// @notice Returns the current total unclaimed bounty
    /// @dev Set back to zero once the bounty has been claimed
    /// @param question_id The ID of the question
    function getBounty(bytes32 question_id)
    public view returns(uint256) {
        return questions[question_id].bounty;
    }

    /// @notice Returns the current best answer
    /// @param question_id The ID of the question
    function getBestAnswer(bytes32 question_id)
    public view returns(bytes32) {
        return questions[question_id].best_answer;
    }

    /// @notice Returns the history hash of the question
    /// @param question_id The ID of the question
    /// @dev Updated on each answer, then rewound as each is claimed
    function getHistoryHash(bytes32 question_id)
    public view returns(bytes32) {
        return questions[question_id].history_hash;
    }

    /// @notice Returns the highest bond posted so far for a question
    /// @param question_id The ID of the question
    function getBond(bytes32 question_id)
    public view returns(uint256) {
        return questions[question_id].bond;
    }

}

// File: @openzeppelin/contracts/math/SafeMath.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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 (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @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) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @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) {
        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, reverting 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) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting 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) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * 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, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * 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, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

// File: contracts/PredictionMarketV2.sol

pragma solidity ^0.6.0;


// openzeppelin imports


library CeilDiv {
  // calculates ceil(x/y)
  function ceildiv(uint256 x, uint256 y) internal pure returns (uint256) {
    if (x > 0) return ((x - 1) / y) + 1;
    return x / y;
  }
}

/// @title Market Contract Factory
contract PredictionMarketV2 {
  using SafeMath for uint256;
  using CeilDiv for uint256;

  // ------ Events ------

  event MarketCreated(address indexed user, uint256 indexed marketId, uint256 outcomes, string question, string image);

  event MarketActionTx(
    address indexed user,
    MarketAction indexed action,
    uint256 indexed marketId,
    uint256 outcomeId,
    uint256 shares,
    uint256 value,
    uint256 timestamp
  );

  event MarketOutcomePrice(uint256 indexed marketId, uint256 indexed outcomeId, uint256 value, uint256 timestamp);

  event MarketLiquidity(
    uint256 indexed marketId,
    uint256 value, // total liquidity
    uint256 price, // value of one liquidity share; max: 1 (50-50 situation)
    uint256 timestamp
  );

  event MarketResolved(address indexed user, uint256 indexed marketId, uint256 outcomeId, uint256 timestamp);

  // ------ Events End ------

  uint256 public constant MAX_UINT_256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935;

  uint256 public constant ONE = 10**18;

  enum MarketState {
    open,
    closed,
    resolved
  }
  enum MarketAction {
    buy,
    sell,
    addLiquidity,
    removeLiquidity,
    claimWinnings,
    claimLiquidity,
    claimFees,
    claimVoided
  }

  struct Market {
    // market details
    uint256 closesAtTimestamp;
    uint256 balance; // total stake
    uint256 liquidity; // stake held
    uint256 sharesAvailable; // shares held (all outcomes)
    mapping(address => uint256) liquidityShares;
    mapping(address => bool) liquidityClaims; // wether user has claimed liquidity earnings
    MarketState state; // resolution variables
    MarketResolution resolution; // fees
    MarketFees fees;
    // market outcomes
    uint256[] outcomeIds;
    mapping(uint256 => MarketOutcome) outcomes;
    IERC20R token; // ERC20 token market will use for trading
  }

  struct MarketFees {
    uint256 value; // fee % taken from every transaction
    uint256 poolWeight; // internal var used to ensure pro-rate fee distribution
    mapping(address => uint256) claimed;
  }

  struct MarketResolution {
    bool resolved;
    uint256 outcomeId;
    bytes32 questionId; // realitio questionId
  }

  struct MarketOutcome {
    uint256 marketId;
    uint256 id;
    Shares shares;
  }

  struct Shares {
    uint256 total; // number of shares
    uint256 available; // available shares
    mapping(address => uint256) holders;
    mapping(address => bool) claims; // wether user has claimed winnings
    mapping(address => bool) voidedClaims; // wether user has claimed voided market shares
  }

  struct CreateMarketArgs {
    uint256 value;
    uint256 closesAt;
    uint256 outcomes;
    IERC20R token;
    uint256[] distribution;
    string question;
    string image;
    address arbitrator;
  }

  uint256[] marketIds;
  mapping(uint256 => Market) markets;
  uint256 public marketIndex;

  // governance
  uint256 public fee; // fee % taken from every transaction
  // realitio configs
  address public realitioAddress;
  uint256 public realitioTimeout;
  // market creation
  IERC20R public requiredBalanceToken; // token used for rewards / market creation
  uint256 public requiredBalance; // required balance for market creation

  // ------ Modifiers ------

  modifier isMarket(uint256 marketId) {
    require(marketId < marketIndex, "Market not found");
    _;
  }

  modifier timeTransitions(uint256 marketId) {
    if (now > markets[marketId].closesAtTimestamp && markets[marketId].state == MarketState.open) {
      nextState(marketId);
    }
    _;
  }

  modifier atState(uint256 marketId, MarketState state) {
    require(markets[marketId].state == state, "Market in incorrect state");
    _;
  }

  modifier notAtState(uint256 marketId, MarketState state) {
    require(markets[marketId].state != state, "Market in incorrect state");
    _;
  }

  modifier transitionNext(uint256 marketId) {
    _;
    nextState(marketId);
  }

  modifier mustHoldRequiredBalance() {
    require(
      requiredBalance == 0 || requiredBalanceToken.balanceOf(msg.sender) >= requiredBalance,
      "msg.sender must hold minimum erc20 balance"
    );
    _;
  }

  // ------ Modifiers End ------

  /// @dev protocol is immutable and has no ownership
  constructor(
    uint256 _fee,
    IERC20R _requiredBalanceToken,
    uint256 _requiredBalance,
    address _realitioAddress,
    uint256 _realitioTimeout
  ) public {
    require(_realitioAddress != address(0), "_realitioAddress is address 0");
    require(_realitioTimeout > 0, "timeout must be positive");

    fee = _fee;
    requiredBalanceToken = _requiredBalanceToken;
    requiredBalance = _requiredBalance;
    realitioAddress = _realitioAddress;
    realitioTimeout = _realitioTimeout;
  }

  // ------ Core Functions ------

  /// @dev Creates a market, initializes the outcome shares pool and submits a question in Realitio
  function createMarket(CreateMarketArgs calldata args) external mustHoldRequiredBalance returns (uint256) {
    uint256 marketId = marketIndex;
    marketIds.push(marketId);

    Market storage market = markets[marketId];

    require(args.value > 0, "stake needs to be > 0");
    require(args.closesAt > now, "market must resolve after the current date");
    require(args.arbitrator != address(0), "invalid arbitrator address");
    require(args.outcomes <= 2 ** 8, "number of outcomes has to be less or equal than 256");

    market.token = args.token;
    market.closesAtTimestamp = args.closesAt;
    market.state = MarketState.open;
    market.fees.value = fee;
    // setting intial value to an integer that does not map to any outcomeId
    market.resolution.outcomeId = MAX_UINT_256;

    // creating market outcomes
    for (uint256 i = 0; i < args.outcomes; i++) {
      market.outcomeIds.push(i);
      MarketOutcome storage outcome = market.outcomes[i];

      outcome.marketId = marketId;
      outcome.id = i;
    }

    // creating question in realitio
    RealitioERC20(realitioAddress).askQuestionERC20(
      2,
      args.question,
      args.arbitrator,
      uint32(realitioTimeout),
      uint32(args.closesAt),
      0,
      0
    );

    addLiquidity(marketId, args.value, args.distribution);

    // emiting initial price events
    emitMarketOutcomePriceEvents(marketId);
    emit MarketCreated(msg.sender, marketId, args.outcomes, args.question, args.image);

    // incrementing market array index
    marketIndex = marketIndex + 1;

    return marketId;
  }

  /// @dev Calculates the number of shares bought with "amount" balance
  function calcBuyAmount(
    uint256 amount,
    uint256 marketId,
    uint256 outcomeId
  ) public view returns (uint256) {
    Market storage market = markets[marketId];

    uint256[] memory outcomesShares = getMarketOutcomesShares(marketId);
    uint256 amountMinusFees = amount.sub(amount.mul(market.fees.value) / ONE);
    uint256 buyTokenPoolBalance = outcomesShares[outcomeId];
    uint256 endingOutcomeBalance = buyTokenPoolBalance.mul(ONE);
    for (uint256 i = 0; i < outcomesShares.length; i++) {
      if (i != outcomeId) {
        uint256 outcomeShares = outcomesShares[i];
        endingOutcomeBalance = endingOutcomeBalance.mul(outcomeShares).ceildiv(outcomeShares.add(amountMinusFees));
      }
    }
    require(endingOutcomeBalance > 0, "must have non-zero balances");

    return buyTokenPoolBalance.add(amountMinusFees).sub(endingOutcomeBalance.ceildiv(ONE));
  }

  /// @dev Calculates the number of shares needed to be sold in order to receive "amount" in balance
  function calcSellAmount(
    uint256 amount,
    uint256 marketId,
    uint256 outcomeId
  ) public view returns (uint256 outcomeTokenSellAmount) {
    Market storage market = markets[marketId];

    uint256[] memory outcomesShares = getMarketOutcomesShares(marketId);
    uint256 amountPlusFees = amount.mul(ONE) / ONE.sub(market.fees.value);
    uint256 sellTokenPoolBalance = outcomesShares[outcomeId];
    uint256 endingOutcomeBalance = sellTokenPoolBalance.mul(ONE);
    for (uint256 i = 0; i < outcomesShares.length; i++) {
      if (i != outcomeId) {
        uint256 outcomeShares = outcomesShares[i];
        endingOutcomeBalance = endingOutcomeBalance.mul(outcomeShares).ceildiv(outcomeShares.sub(amountPlusFees));
      }
    }
    require(endingOutcomeBalance > 0, "must have non-zero balances");

    return amountPlusFees.add(endingOutcomeBalance.ceildiv(ONE)).sub(sellTokenPoolBalance);
  }

  /// @dev Buy shares of a market outcome
  function buy(
    uint256 marketId,
    uint256 outcomeId,
    uint256 minOutcomeSharesToBuy,
    uint256 value
  ) external timeTransitions(marketId) atState(marketId, MarketState.open) {
    Market storage market = markets[marketId];

    uint256 shares = calcBuyAmount(value, marketId, outcomeId);
    require(shares >= minOutcomeSharesToBuy, "minimum buy amount not reached");
    require(shares > 0, "shares amount is 0");

    // subtracting fee from transaction value
    uint256 feeAmount = value.mul(market.fees.value) / ONE;
    market.fees.poolWeight = market.fees.poolWeight.add(feeAmount);
    uint256 valueMinusFees = value.sub(feeAmount);

    MarketOutcome storage outcome = market.outcomes[outcomeId];

    // Funding market shares with received funds
    addSharesToMarket(marketId, valueMinusFees);

    require(outcome.shares.available >= shares, "outcome shares pool balance is too low");

    transferOutcomeSharesfromPool(msg.sender, marketId, outcomeId, shares);

    require(market.token.transferFrom(msg.sender, address(this), value), "erc20 transfer failed");

    emit MarketActionTx(msg.sender, MarketAction.buy, marketId, outcomeId, shares, value, now);
    emitMarketOutcomePriceEvents(marketId);
  }

  /// @dev Sell shares of a market outcome
  function sell(
    uint256 marketId,
    uint256 outcomeId,
    uint256 value,
    uint256 maxOutcomeSharesToSell
  ) external timeTransitions(marketId) atState(marketId, MarketState.open) {
    Market storage market = markets[marketId];
    MarketOutcome storage outcome = market.outcomes[outcomeId];

    uint256 shares = calcSellAmount(value, marketId, outcomeId);

    require(shares <= maxOutcomeSharesToSell, "maximum sell amount exceeded");
    require(shares > 0, "shares amount is 0");
    require(outcome.shares.holders[msg.sender] >= shares, "user does not have enough balance");

    transferOutcomeSharesToPool(msg.sender, marketId, outcomeId, shares);

    // adding fee to transaction value
    uint256 feeAmount = value.mul(market.fees.value) / (ONE.sub(fee));
    market.fees.poolWeight = market.fees.poolWeight.add(feeAmount);
    uint256 valuePlusFees = value.add(feeAmount);

    require(market.balance >= valuePlusFees, "market does not have enough balance");

    // Rebalancing market shares
    removeSharesFromMarket(marketId, valuePlusFees);

    // Transferring funds to user
    require(market.token.transfer(msg.sender, value), "erc20 transfer failed");

    emit MarketActionTx(msg.sender, MarketAction.sell, marketId, outcomeId, shares, value, now);
    emitMarketOutcomePriceEvents(marketId);
  }

  /// @dev Adds liquidity to a market - external
  function addLiquidity(
    uint256 marketId,
    uint256 value,
    uint256[] memory distribution
  ) public timeTransitions(marketId) atState(marketId, MarketState.open) {
    Market storage market = markets[marketId];

    require(value > 0, "stake has to be greater than 0.");

    uint256 liquidityAmount;

    uint256[] memory outcomesShares = getMarketOutcomesShares(marketId);
    uint256[] memory sendBackAmounts = new uint256[](outcomesShares.length);
    uint256 poolWeight = 0;

    if (market.liquidity > 0) {
      require(distribution.length == 0, "market already has liquidity, can't distribute liquidity");

      // part of the liquidity is exchanged for outcome shares if market is not balanced
      for (uint256 i = 0; i < outcomesShares.length; i++) {
        uint256 outcomeShares = outcomesShares[i];
        if (poolWeight < outcomeShares) poolWeight = outcomeShares;
      }

      for (uint256 i = 0; i < outcomesShares.length; i++) {
        uint256 remaining = value.mul(outcomesShares[i]) / poolWeight;
        sendBackAmounts[i] = value.sub(remaining);
      }

      liquidityAmount = value.mul(market.liquidity) / poolWeight;

      // re-balancing fees pool
      rebalanceFeesPool(marketId, liquidityAmount, MarketAction.addLiquidity);
    } else {
      // funding market with no liquidity
      if (distribution.length > 0) {
        require(distribution.length == outcomesShares.length, "weight distribution length does not match");

        uint256 maxHint = 0;
        for (uint256 i = 0; i < distribution.length; i++) {
          uint256 hint = distribution[i];
          if (maxHint < hint) maxHint = hint;
        }

        for (uint256 i = 0; i < distribution.length; i++) {
          uint256 remaining = value.mul(distribution[i]) / maxHint;
          require(remaining > 0, "must hint a valid distribution");
          sendBackAmounts[i] = value.sub(remaining);
        }
      }

      // funding market with total liquidity amount
      liquidityAmount = value;
    }

    // funding market
    market.liquidity = market.liquidity.add(liquidityAmount);
    market.liquidityShares[msg.sender] = market.liquidityShares[msg.sender].add(liquidityAmount);

    addSharesToMarket(marketId, value);

    // transform sendBackAmounts to array of amounts added
    for (uint256 i = 0; i < sendBackAmounts.length; i++) {
      if (sendBackAmounts[i] > 0) {
        uint256 marketShares = market.sharesAvailable;
        uint256 outcomeShares = market.outcomes[i].shares.available;
        transferOutcomeSharesfromPool(msg.sender, marketId, i, sendBackAmounts[i]);
        emit MarketActionTx(
          msg.sender,
          MarketAction.buy,
          marketId,
          i,
          sendBackAmounts[i],
          (marketShares.sub(outcomeShares)).mul(sendBackAmounts[i]).div(market.sharesAvailable), // price * shares
          now
        );
      }
    }

    uint256 liquidityPrice = getMarketLiquidityPrice(marketId);
    uint256 liquidityValue = liquidityPrice.mul(liquidityAmount) / ONE;

    require(market.token.transferFrom(msg.sender, address(this), value), "erc20 transfer failed");

    emit MarketActionTx(msg.sender, MarketAction.addLiquidity, marketId, 0, liquidityAmount, liquidityValue, now);
    emit MarketLiquidity(marketId, market.liquidity, liquidityPrice, now);
  }

  /// @dev Removes liquidity to a market - external
  function removeLiquidity(uint256 marketId, uint256 shares)
    external
    timeTransitions(marketId)
    atState(marketId, MarketState.open)
  {
    Market storage market = markets[marketId];

    require(market.liquidityShares[msg.sender] >= shares, "user does not have enough balance");
    // claiming any pending fees
    claimFees(marketId);

    // re-balancing fees pool
    rebalanceFeesPool(marketId, shares, MarketAction.removeLiquidity);

    uint256[] memory outcomesShares = getMarketOutcomesShares(marketId);
    uint256[] memory sendAmounts = new uint256[](outcomesShares.length);
    uint256 poolWeight = MAX_UINT_256;

    // part of the liquidity is exchanged for outcome shares if market is not balanced
    for (uint256 i = 0; i < outcomesShares.length; i++) {
      uint256 outcomeShares = outcomesShares[i];
      if (poolWeight > outcomeShares) poolWeight = outcomeShares;
    }

    uint256 liquidityAmount = shares.mul(poolWeight).div(market.liquidity);

    for (uint256 i = 0; i < outcomesShares.length; i++) {
      sendAmounts[i] = outcomesShares[i].mul(shares) / market.liquidity;
      sendAmounts[i] = sendAmounts[i].sub(liquidityAmount);
    }

    // removing liquidity from market
    removeSharesFromMarket(marketId, liquidityAmount);
    market.liquidity = market.liquidity.sub(shares);
    // removing liquidity tokens from market creator
    market.liquidityShares[msg.sender] = market.liquidityShares[msg.sender].sub(shares);

    for (uint256 i = 0; i < outcomesShares.length; i++) {
      if (sendAmounts[i] > 0) {
        uint256 marketShares = market.sharesAvailable;
        uint256 outcomeShares = market.outcomes[i].shares.available;

        transferOutcomeSharesfromPool(msg.sender, marketId, i, sendAmounts[i]);
        emit MarketActionTx(
          msg.sender,
          MarketAction.buy,
          marketId,
          i,
          sendAmounts[i],
          (marketShares.sub(outcomeShares)).mul(sendAmounts[i]).div(market.sharesAvailable), // price * shares
          now
        );
      }
    }

    // transferring user funds from liquidity removed
    require(market.token.transfer(msg.sender, liquidityAmount), "erc20 transfer failed");

    emit MarketActionTx(msg.sender, MarketAction.removeLiquidity, marketId, 0, shares, liquidityAmount, now);
    emit MarketLiquidity(marketId, market.liquidity, getMarketLiquidityPrice(marketId), now);
  }

  /// @dev Fetches winning outcome from Realitio and resolves the market
  function resolveMarketOutcome(uint256 marketId)
    external
    timeTransitions(marketId)
    atState(marketId, MarketState.closed)
    transitionNext(marketId)
    returns (uint256)
  {
    Market storage market = markets[marketId];

    RealitioERC20 realitio = RealitioERC20(realitioAddress);
    // will fail if question is not finalized
    uint256 outcomeId = uint256(realitio.resultFor(market.resolution.questionId));

    market.resolution.outcomeId = outcomeId;

    emit MarketResolved(msg.sender, marketId, outcomeId, now);
    emitMarketOutcomePriceEvents(marketId);

    return market.resolution.outcomeId;
  }

  /// @dev Allows holders of resolved outcome shares to claim earnings.
  function claimWinnings(uint256 marketId) external atState(marketId, MarketState.resolved) {
    Market storage market = markets[marketId];
    MarketOutcome storage resolvedOutcome = market.outcomes[market.resolution.outcomeId];

    require(resolvedOutcome.shares.holders[msg.sender] > 0, "user does not hold resolved outcome shares");
    require(resolvedOutcome.shares.claims[msg.sender] == false, "user already claimed resolved outcome winnings");

    // 1 share => price = 1
    uint256 value = resolvedOutcome.shares.holders[msg.sender];

    // assuring market has enough funds
    require(market.balance >= value, "Market does not have enough balance");

    market.balance = market.balance.sub(value);
    resolvedOutcome.shares.claims[msg.sender] = true;

    emit MarketActionTx(
      msg.sender,
      MarketAction.claimWinnings,
      marketId,
      market.resolution.outcomeId,
      resolvedOutcome.shares.holders[msg.sender],
      value,
      now
    );

    require(market.token.transfer(msg.sender, value), "erc20 transfer failed");
  }

  /// @dev Allows holders of voided outcome shares to claim balance back.
  function claimVoidedOutcomeShares(uint256 marketId, uint256 outcomeId)
    external
    atState(marketId, MarketState.resolved)
  {
    Market storage market = markets[marketId];
    MarketOutcome storage outcome = market.outcomes[outcomeId];

    require(outcome.shares.holders[msg.sender] > 0, "user does not hold outcome shares");
    require(outcome.shares.voidedClaims[msg.sender] == false, "user already claimed outcome shares");

    // voided market - shares are valued at last market price
    uint256 price = getMarketOutcomePrice(marketId, outcomeId);
    uint256 value = price.mul(outcome.shares.holders[msg.sender]).div(ONE);

    // assuring market has enough funds
    require(market.balance >= value, "Market does not have enough balance");

    market.balance = market.balance.sub(value);
    outcome.shares.voidedClaims[msg.sender] = true;

    emit MarketActionTx(
      msg.sender,
      MarketAction.claimVoided,
      marketId,
      outcomeId,
      outcome.shares.holders[msg.sender],
      value,
      now
    );

    require(market.token.transfer(msg.sender, value), "erc20 transfer failed");
  }

  /// @dev Allows liquidity providers to claim earnings from liquidity providing.
  function claimLiquidity(uint256 marketId) external atState(marketId, MarketState.resolved) {
    Market storage market = markets[marketId];

    // claiming any pending fees
    claimFees(marketId);

    require(market.liquidityShares[msg.sender] > 0, "user does not hold liquidity shares");
    require(market.liquidityClaims[msg.sender] == false, "user already claimed liquidity winnings");

    // value = total resolved outcome pool shares * pool share (%)
    uint256 liquidityPrice = getMarketLiquidityPrice(marketId);
    uint256 value = liquidityPrice.mul(market.liquidityShares[msg.sender]) / ONE;

    // assuring market has enough funds
    require(market.balance >= value, "Market does not have enough balance");

    market.balance = market.balance.sub(value);
    market.liquidityClaims[msg.sender] = true;

    emit MarketActionTx(
      msg.sender,
      MarketAction.claimLiquidity,
      marketId,
      0,
      market.liquidityShares[msg.sender],
      value,
      now
    );

    require(market.token.transfer(msg.sender, value), "erc20 transfer failed");
  }

  /// @dev Allows liquidity providers to claim their fees share from fees pool
  function claimFees(uint256 marketId) public {
    Market storage market = markets[marketId];

    uint256 claimableFees = getUserClaimableFees(marketId, msg.sender);

    if (claimableFees > 0) {
      market.fees.claimed[msg.sender] = market.fees.claimed[msg.sender].add(claimableFees);
      require(market.token.transfer(msg.sender, claimableFees), "erc20 transfer failed");
    }

    emit MarketActionTx(
      msg.sender,
      MarketAction.claimFees,
      marketId,
      0,
      market.liquidityShares[msg.sender],
      claimableFees,
      now
    );
  }

  /// @dev Rebalances the fees pool. Needed in every AddLiquidity / RemoveLiquidity call
  function rebalanceFeesPool(
    uint256 marketId,
    uint256 liquidityShares,
    MarketAction action
  ) private returns (uint256) {
    Market storage market = markets[marketId];

    uint256 poolWeight = liquidityShares.mul(market.fees.poolWeight).div(market.liquidity);

    if (action == MarketAction.addLiquidity) {
      market.fees.poolWeight = market.fees.poolWeight.add(poolWeight);
      market.fees.claimed[msg.sender] = market.fees.claimed[msg.sender].add(poolWeight);
    } else {
      market.fees.poolWeight = market.fees.poolWeight.sub(poolWeight);
      market.fees.claimed[msg.sender] = market.fees.claimed[msg.sender].sub(poolWeight);
    }
  }

  /// @dev Transitions market to next state
  function nextState(uint256 marketId) private {
    Market storage market = markets[marketId];
    market.state = MarketState(uint256(market.state) + 1);
  }

  /// @dev Emits a outcome price event for every outcome
  function emitMarketOutcomePriceEvents(uint256 marketId) private {
    Market storage market = markets[marketId];

    for (uint256 i = 0; i < market.outcomeIds.length; i++) {
      emit MarketOutcomePrice(marketId, i, getMarketOutcomePrice(marketId, i), now);
    }

    // liquidity shares also change value
    emit MarketLiquidity(marketId, market.liquidity, getMarketLiquidityPrice(marketId), now);
  }

  /// @dev Adds outcome shares to shares pool
  function addSharesToMarket(uint256 marketId, uint256 shares) private {
    Market storage market = markets[marketId];

    for (uint256 i = 0; i < market.outcomeIds.length; i++) {
      MarketOutcome storage outcome = market.outcomes[i];

      outcome.shares.available = outcome.shares.available.add(shares);
      outcome.shares.total = outcome.shares.total.add(shares);

      // only adding to market total shares, the available remains
      market.sharesAvailable = market.sharesAvailable.add(shares);
    }

    market.balance = market.balance.add(shares);
  }

  /// @dev Removes outcome shares from shares pool
  function removeSharesFromMarket(uint256 marketId, uint256 shares) private {
    Market storage market = markets[marketId];

    for (uint256 i = 0; i < market.outcomeIds.length; i++) {
      MarketOutcome storage outcome = market.outcomes[i];

      outcome.shares.available = outcome.shares.available.sub(shares);
      outcome.shares.total = outcome.shares.total.sub(shares);

      // only subtracting from market total shares, the available remains
      market.sharesAvailable = market.sharesAvailable.sub(shares);
    }

    market.balance = market.balance.sub(shares);
  }

  /// @dev Transfer outcome shares from pool to user balance
  function transferOutcomeSharesfromPool(
    address user,
    uint256 marketId,
    uint256 outcomeId,
    uint256 shares
  ) private {
    Market storage market = markets[marketId];
    MarketOutcome storage outcome = market.outcomes[outcomeId];

    // transfering shares from shares pool to user
    outcome.shares.holders[user] = outcome.shares.holders[user].add(shares);
    outcome.shares.available = outcome.shares.available.sub(shares);
    market.sharesAvailable = market.sharesAvailable.sub(shares);
  }

  /// @dev Transfer outcome shares from user balance back to pool
  function transferOutcomeSharesToPool(
    address user,
    uint256 marketId,
    uint256 outcomeId,
    uint256 shares
  ) private {
    Market storage market = markets[marketId];
    MarketOutcome storage outcome = market.outcomes[outcomeId];

    // adding shares back to pool
    outcome.shares.holders[user] = outcome.shares.holders[user].sub(shares);
    outcome.shares.available = outcome.shares.available.add(shares);
    market.sharesAvailable = market.sharesAvailable.add(shares);
  }

  // ------ Core Functions End ------

  // ------ Getters ------

  function getUserMarketShares(uint256 marketId, address user)
    external
    view
    returns (
      uint256,
      uint256[] memory
    )
  {
    Market storage market = markets[marketId];
    uint256[] memory outcomeShares = new uint256[](market.outcomeIds.length);

    for (uint256 i = 0; i < market.outcomeIds.length; i++) {
      outcomeShares[i] = market.outcomes[i].shares.holders[user];
    }

    return (
      market.liquidityShares[user],
      outcomeShares
    );
  }

  function getUserClaimStatus(uint256 marketId, address user)
    external
    view
    returns (
      bool,
      bool,
      bool,
      bool,
      uint256
    )
  {
    Market storage market = markets[marketId];

    // market still not resolved
    if (market.state != MarketState.resolved) {
      return (false, false, false, false, getUserClaimableFees(marketId, user));
    }

    MarketOutcome storage outcome = market.outcomes[market.resolution.outcomeId];

    return (
      outcome.shares.holders[user] > 0,
      outcome.shares.claims[user],
      market.liquidityShares[user] > 0,
      market.liquidityClaims[user],
      getUserClaimableFees(marketId, user)
    );
  }

  function getUserLiquidityPoolShare(uint256 marketId, address user) external view returns (uint256) {
    Market storage market = markets[marketId];

    return market.liquidityShares[user].mul(ONE).div(market.liquidity);
  }

  function getUserClaimableFees(uint256 marketId, address user) public view returns (uint256) {
    Market storage market = markets[marketId];

    uint256 rawAmount = market.fees.poolWeight.mul(market.liquidityShares[user]).div(market.liquidity);

    // No fees left to claim
    if (market.fees.claimed[user] > rawAmount) return 0;

    return rawAmount.sub(market.fees.claimed[user]);
  }

  function getMarkets() external view returns (uint256[] memory) {
    return marketIds;
  }

  function getMarketData(uint256 marketId)
    external
    view
    returns (
      MarketState,
      uint256,
      uint256,
      uint256,
      uint256,
      int256
    )
  {
    Market storage market = markets[marketId];

    return (
      market.state,
      market.closesAtTimestamp,
      market.liquidity,
      market.balance,
      market.sharesAvailable,
      getMarketResolvedOutcome(marketId)
    );
  }

  function getMarketAltData(uint256 marketId)
    external
    view
    returns (
      uint256,
      bytes32,
      uint256
    )
  {
    Market storage market = markets[marketId];

    return (market.fees.value, market.resolution.questionId, uint256(market.resolution.questionId));
  }

  function getMarketQuestion(uint256 marketId) external view returns (bytes32) {
    Market storage market = markets[marketId];

    return (market.resolution.questionId);
  }

  function getMarketPrices(uint256 marketId)
    external
    view
    returns (
      uint256,
      uint256[] memory
    )
  {
    Market storage market = markets[marketId];
    uint256[] memory prices = new uint256[](market.outcomeIds.length);

    for (uint256 i = 0; i < market.outcomeIds.length; i++) {
      prices[i] = getMarketOutcomePrice(marketId, i);
    }

    return (getMarketLiquidityPrice(marketId), prices);
  }

  function getMarketLiquidityPrice(uint256 marketId) public view returns (uint256) {
    Market storage market = markets[marketId];

    if (market.state == MarketState.resolved && !isMarketVoided(marketId)) {
      // resolved market, outcome prices are either 0 or 1
      // final liquidity price = outcome shares / liquidity shares
      return market.outcomes[market.resolution.outcomeId].shares.available.mul(ONE).div(market.liquidity);
    }

    // liquidity price = product(every outcome shares) / sum (every outcomeOddsWeight) * # outcomes / liquidity shares
    uint256 marketSharesProduct = ONE;
    uint256 marketSharesSum = 0;

    for (uint256 i = 0; i < market.outcomeIds.length; i++) {
      MarketOutcome storage outcome = market.outcomes[i];

      marketSharesProduct = marketSharesProduct.mul(outcome.shares.available).div(ONE);
      marketSharesSum = marketSharesSum.add(getOutcomeOddsWeight(marketId, i));
    }

    return marketSharesProduct.mul(market.outcomeIds.length).mul(ONE).div(marketSharesSum).mul(ONE).div(market.liquidity);
  }

  function getMarketResolvedOutcome(uint256 marketId) public view returns (int256) {
    Market storage market = markets[marketId];

    // returning -1 if market still not resolved
    if (market.state != MarketState.resolved) {
      return -1;
    }

    return int256(market.resolution.outcomeId);
  }

  function isMarketVoided(uint256 marketId) public view returns (bool) {
    Market storage market = markets[marketId];

    // market still not resolved, still in valid state
    if (market.state != MarketState.resolved) {
      return false;
    }

    // resolved market id does not match any of the market ids
    return market.resolution.outcomeId >= market.outcomeIds.length;
  }

  // ------ Outcome Getters ------

  function getMarketOutcomeIds(uint256 marketId) external view returns (uint256[] memory) {
    Market storage market = markets[marketId];
    return market.outcomeIds;
  }

  function getOutcomeOddsWeight(uint256 marketId, uint256 outcomeId) public view returns (uint256) {
    Market storage market = markets[marketId];

    uint256 productWeight = ONE;

    for (uint256 i = 0; i < market.outcomeIds.length; i++) {
      if (i == outcomeId) continue;

      productWeight = productWeight.mul(market.outcomes[i].shares.available).div(ONE);
    }

    return productWeight;
  }

  function getMarketOutcomePrice(uint256 marketId, uint256 outcomeId) public view returns (uint256) {
    Market storage market = markets[marketId];

    if (market.state == MarketState.resolved && !isMarketVoided(marketId)) {
      // resolved market, price is either 0 or 1
      return outcomeId == market.resolution.outcomeId ? ONE : 0;
    }

    uint256 sumOutcomeOddsWeight = 0;
    for (uint256 i = 0; i < market.outcomeIds.length; i++) {
      sumOutcomeOddsWeight = sumOutcomeOddsWeight.add(getOutcomeOddsWeight(marketId, i));
    }

    // outcome price = outcomeOddsWeight / sum(every outcomeOddsWeight)
    return getOutcomeOddsWeight(marketId, outcomeId).mul(ONE).div(sumOutcomeOddsWeight);
  }

  function getMarketOutcomeData(uint256 marketId, uint256 outcomeId)
    external
    view
    returns (
      uint256,
      uint256,
      uint256
    )
  {
    Market storage market = markets[marketId];
    MarketOutcome storage outcome = market.outcomes[outcomeId];

    return (getMarketOutcomePrice(marketId, outcomeId), outcome.shares.available, outcome.shares.total);
  }

  function getMarketOutcomesShares(uint256 marketId) private view returns (uint256[] memory) {
    Market storage market = markets[marketId];

    uint256[] memory shares = new uint256[](market.outcomeIds.length);
    for (uint256 i = 0; i < market.outcomeIds.length; i++) {
      shares[i] = market.outcomes[i].shares.available;
    }

    return shares;
  }
}

// File: @openzeppelin/contracts/utils/EnumerableSet.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;

        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            bytes32 lastvalue = set._values[lastIndex];

            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        require(set._values.length > index, "EnumerableSet: index out of bounds");
        return set._values[index];
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }


    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

   /**
    * @dev Returns the value stored at position `index` in the set. O(1).
    *
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }
}

// File: @openzeppelin/contracts/utils/Address.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: @openzeppelin/contracts/utils/Context.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

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

// File: @openzeppelin/contracts/access/AccessControl.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;




/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it.
 */
abstract contract AccessControl is Context {
    using EnumerableSet for EnumerableSet.AddressSet;
    using Address for address;

    struct RoleData {
        EnumerableSet.AddressSet members;
        bytes32 adminRole;
    }

    mapping (bytes32 => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     *
     * _Available since v3.1._
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view returns (bool) {
        return _roles[role].members.contains(account);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view returns (uint256) {
        return _roles[role].members.length();
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
        return _roles[role].members.at(index);
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");

        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");

        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public virtual {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");

        _revokeRole(role, account);
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (_roles[role].members.add(account)) {
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (_roles[role].members.remove(account)) {
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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);
}

// File: @openzeppelin/contracts/token/ERC20/ERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;




/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;

    mapping (address => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;
    uint8 private _decimals;

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory name_, string memory symbol_) public {
        _name = name_;
        _symbol = symbol_;
        _decimals = 18;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return _decimals;
    }

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

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override 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 virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

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

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override 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 virtual override 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 virtual 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 virtual 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 virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        _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 virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _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 virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        _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 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 virtual {
        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 Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal virtual {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}

// File: @openzeppelin/contracts/token/ERC20/ERC20Burnable.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;



/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20Burnable is Context, ERC20 {
    using SafeMath for uint256;

    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");

        _approve(account, _msgSender(), decreasedAllowance);
        _burn(account, amount);
    }
}

// File: @openzeppelin/contracts/utils/Pausable.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;


/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor () internal {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// File: @openzeppelin/contracts/token/ERC20/ERC20Pausable.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;



/**
 * @dev ERC20 token with pausable token transfers, minting and burning.
 *
 * Useful for scenarios such as preventing trades until the end of an evaluation
 * period, or having an emergency switch for freezing all token transfers in the
 * event of a large bug.
 */
abstract contract ERC20Pausable is ERC20, Pausable {
    /**
     * @dev See {ERC20-_beforeTokenTransfer}.
     *
     * Requirements:
     *
     * - the contract must not be paused.
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
        super._beforeTokenTransfer(from, to, amount);

        require(!paused(), "ERC20Pausable: token transfer while paused");
    }
}

// File: @openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;






/**
 * @dev {ERC20} token, including:
 *
 *  - ability for holders to burn (destroy) their tokens
 *  - a minter role that allows for token minting (creation)
 *  - a pauser role that allows to stop all token transfers
 *
 * This contract uses {AccessControl} to lock permissioned functions using the
 * different roles - head to its documentation for details.
 *
 * The account that deploys the contract will be granted the minter and pauser
 * roles, as well as the default admin role, which will let it grant both minter
 * and pauser roles to other accounts.
 */
contract ERC20PresetMinterPauser is Context, AccessControl, ERC20Burnable, ERC20Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    /**
     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
     * account that deploys the contract.
     *
     * See {ERC20-constructor}.
     */
    constructor(string memory name, string memory symbol) public ERC20(name, symbol) {
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
    }

    /**
     * @dev Creates `amount` new tokens for `to`.
     *
     * See {ERC20-_mint}.
     *
     * Requirements:
     *
     * - the caller must have the `MINTER_ROLE`.
     */
    function mint(address to, uint256 amount) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
        _mint(to, amount);
    }

    /**
     * @dev Pauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_pause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function pause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
        _pause();
    }

    /**
     * @dev Unpauses all token transfers.
     *
     * See {ERC20Pausable} and {Pausable-_unpause}.
     *
     * Requirements:
     *
     * - the caller must have the `PAUSER_ROLE`.
     */
    function unpause() public virtual {
        require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause");
        _unpause();
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20, ERC20Pausable) {
        super._beforeTokenTransfer(from, to, amount);
    }
}

// File: contracts/FantasyERC20.sol

pragma solidity ^0.6.0;


contract FantasyERC20 is ERC20PresetMinterPauser {

  event TokensClaimed(
    address indexed user,
    uint256 amount,
    uint256 timestamp
  );

  mapping(address => bool) usersClaimed;
  address public tokenManager;
  uint256 public tokenAmountToClaim;

  // ------ Modifiers ------

  modifier shouldNotHaveClaimedYet() {
    require(usersClaimed[msg.sender] == false, "FantasyERC20: address already claimed the tokens");
    _;
  }

  constructor(
    string memory name,
    string memory symbol,
    uint256 _tokenAmountToClaim,
    address _tokenManager
    ) public ERC20PresetMinterPauser(name, symbol) {
      tokenAmountToClaim = _tokenAmountToClaim;
      tokenManager = _tokenManager;
    }

  /// @dev Validates if the transfer is from or to the tokenManager, blocking it otherwise
  function _transfer(
    address from,
    address to,
    uint256 amount
  ) internal override {
    require(from == tokenManager || to == tokenManager, "FantasyERC20: token transfer not allowed between the addresses");

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

  /// @dev Allows user to claim the amount of tokens by minting them
  function claimTokens() shouldNotHaveClaimedYet public {
    _mint(msg.sender, tokenAmountToClaim);

    usersClaimed[msg.sender] = true;

    emit TokensClaimed(msg.sender, tokenAmountToClaim, now);
  }

  /// @dev Allows user to approve and claim the amount of tokens by minting them
  function claimAndApproveTokens() shouldNotHaveClaimedYet external {
    claimTokens();
    approve(tokenManager, 2 ** 128 - 1);
  }

  /// @dev Returns if the address has already claimed or not the tokens
  function hasUserClaimedTokens(address user) external view returns (bool) {
    return usersClaimed[user];
  }
}

// File: @openzeppelin/contracts/access/Ownable.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract 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 {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the 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 virtual 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 virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// File: contracts/PredictionMarketResolver.sol

pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;



// openzeppelin imports



contract PredictionMarketResolver is Ownable {
  using SafeMath for uint256;

  event MarketResolved(
    address indexed user,
    uint256 indexed marketId,
    uint256 outcomeId,
    uint256 timestamp
  );

  event MarketClaimed(
    address indexed user,
    uint256 indexed marketId,
    uint256 outcomeId,
    uint256 shares,
    uint256 timestamp
  );

  struct MarketResolution {
    bool resolved;
    uint256 outcomeId;
  }

  mapping(uint256 => mapping(address => bool)) public claims;
  mapping(uint256 => MarketResolution) markets;
  FantasyERC20 public token;
  PredictionMarketV2 public predictionMarket;

  // ------ Modifiers ------

  modifier marketIsResolved(uint256 marketId) {
    require(markets[marketId].resolved, "Market is not resolved");
    _;
  }

  // ------ Modifiers End ------

  // ------ Admin ------

  function setToken(FantasyERC20 _token) external onlyOwner {
    token = _token;
  }

  function setPredictionMarket(PredictionMarketV2 _predictionMarket)
    external
    onlyOwner
  {
    predictionMarket = _predictionMarket;
  }

  function resolveMarket(uint256 marketId, uint256 outcomeId) external onlyOwner {
    markets[marketId] = MarketResolution(true, outcomeId);
    emit MarketResolved(msg.sender, marketId, outcomeId, block.timestamp);
  }

  // ------ Admin End ------

  function getMarketResolution(uint256 marketId)
    external
    view
    returns (MarketResolution memory)
  {
    return markets[marketId];
  }

  function hasUserClaimedMarket(uint256 marketId, address user)
    external
    view
    returns (bool)
  {
    return claims[marketId][user];
  }

  function getUserMarketShares(uint256 marketId, address user)
    public
    view
    returns (uint256[] memory)
  {
    uint[] memory shares;

    // fetching user shares from predictionMarket
    (, shares) = predictionMarket.getUserMarketShares(marketId, user);

    return shares;
  }

  function claim(uint256 marketId, address user) marketIsResolved(marketId) public {
    require(!claims[marketId][user], "Already claimed");

    uint256 outcomeId = markets[marketId].outcomeId;
    uint[] memory shares;

    // fetching user shares from predictionMarket
    (, shares) = predictionMarket.getUserMarketShares(marketId, user);

    // calculating user winnings
    require(shares[outcomeId] > 0, "User has no winning shares");

    claims[marketId][user] = true;
    // minting tokens for user
    token.mint(user, shares[outcomeId]);

    emit MarketClaimed(user, marketId, outcomeId, shares[outcomeId], block.timestamp);
  }

  function claimMultiple(uint256 marketId, address[] calldata users) external {
    for (uint256 i = 0; i < users.length; i++) {
      claim(marketId, users[i]);
    }
  }
}

// File: @openzeppelin/contracts/introspection/IERC165.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;


/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
      * @dev Safely transfers `tokenId` token from `from` to `to`.
      *
      * Requirements:
      *
      * - `from` cannot be the zero address.
      * - `to` cannot be the zero address.
      * - `tokenId` token must exist and be owned by `from`.
      * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
      * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
      *
      * Emits a {Transfer} event.
      */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}

// File: @openzeppelin/contracts/token/ERC721/IERC721Metadata.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;


/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {

    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// File: @openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;


/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {

    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}

// File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}

// File: @openzeppelin/contracts/introspection/ERC165.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;


/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts may inherit from this and call {_registerInterface} to declare
 * their support of an interface.
 */
abstract contract ERC165 is IERC165 {
    /*
     * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
     */
    bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;

    /**
     * @dev Mapping of interface ids to whether or not it's supported.
     */
    mapping(bytes4 => bool) private _supportedInterfaces;

    constructor () internal {
        // Derived contracts need only register support for their own interfaces,
        // we register support for ERC165 itself here
        _registerInterface(_INTERFACE_ID_ERC165);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     *
     * Time complexity O(1), guaranteed to always use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return _supportedInterfaces[interfaceId];
    }

    /**
     * @dev Registers the contract as an implementer of the interface defined by
     * `interfaceId`. Support of the actual ERC165 interface is automatic and
     * registering its interface id is not required.
     *
     * See {IERC165-supportsInterface}.
     *
     * Requirements:
     *
     * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
     */
    function _registerInterface(bytes4 interfaceId) internal virtual {
        require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
        _supportedInterfaces[interfaceId] = true;
    }
}

// File: @openzeppelin/contracts/utils/EnumerableMap.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Library for managing an enumerable variant of Solidity's
 * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
 * type.
 *
 * Maps have the following properties:
 *
 * - Entries are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Entries are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```
 * contract Example {
 *     // Add the library methods
 *     using EnumerableMap for EnumerableMap.UintToAddressMap;
 *
 *     // Declare a set state variable
 *     EnumerableMap.UintToAddressMap private myMap;
 * }
 * ```
 *
 * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
 * supported.
 */
library EnumerableMap {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Map type with
    // bytes32 keys and values.
    // The Map implementation uses private functions, and user-facing
    // implementations (such as Uint256ToAddressMap) are just wrappers around
    // the underlying Map.
    // This means that we can only create new EnumerableMaps for types that fit
    // in bytes32.

    struct MapEntry {
        bytes32 _key;
        bytes32 _value;
    }

    struct Map {
        // Storage of map keys and values
        MapEntry[] _entries;

        // Position of the entry defined by a key in the `entries` array, plus 1
        // because index 0 means a key is not in the map.
        mapping (bytes32 => uint256) _indexes;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
        // We read and store the key's index to prevent multiple reads from the same storage slot
        uint256 keyIndex = map._indexes[key];

        if (keyIndex == 0) { // Equivalent to !contains(map, key)
            map._entries.push(MapEntry({ _key: key, _value: value }));
            // The entry is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            map._indexes[key] = map._entries.length;
            return true;
        } else {
            map._entries[keyIndex - 1]._value = value;
            return false;
        }
    }

    /**
     * @dev Removes a key-value pair from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function _remove(Map storage map, bytes32 key) private returns (bool) {
        // We read and store the key's index to prevent multiple reads from the same storage slot
        uint256 keyIndex = map._indexes[key];

        if (keyIndex != 0) { // Equivalent to contains(map, key)
            // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
            // in the array, and then remove the last entry (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = keyIndex - 1;
            uint256 lastIndex = map._entries.length - 1;

            // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.

            MapEntry storage lastEntry = map._entries[lastIndex];

            // Move the last entry to the index where the entry to delete is
            map._entries[toDeleteIndex] = lastEntry;
            // Update the index for the moved entry
            map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based

            // Delete the slot where the moved entry was stored
            map._entries.pop();

            // Delete the index for the deleted slot
            delete map._indexes[key];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function _contains(Map storage map, bytes32 key) private view returns (bool) {
        return map._indexes[key] != 0;
    }

    /**
     * @dev Returns the number of key-value pairs in the map. O(1).
     */
    function _length(Map storage map) private view returns (uint256) {
        return map._entries.length;
    }

   /**
    * @dev Returns the key-value pair stored at position `index` in the map. O(1).
    *
    * Note that there are no guarantees on the ordering of entries inside the
    * array, and it may change when more entries are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
        require(map._entries.length > index, "EnumerableMap: index out of bounds");

        MapEntry storage entry = map._entries[index];
        return (entry._key, entry._value);
    }

    /**
     * @dev Tries to returns the value associated with `key`.  O(1).
     * Does not revert if `key` is not in the map.
     */
    function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
        uint256 keyIndex = map._indexes[key];
        if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)
        return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based
    }

    /**
     * @dev Returns the value associated with `key`.  O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function _get(Map storage map, bytes32 key) private view returns (bytes32) {
        uint256 keyIndex = map._indexes[key];
        require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
        return map._entries[keyIndex - 1]._value; // All indexes are 1-based
    }

    /**
     * @dev Same as {_get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {_tryGet}.
     */
    function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
        uint256 keyIndex = map._indexes[key];
        require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
        return map._entries[keyIndex - 1]._value; // All indexes are 1-based
    }

    // UintToAddressMap

    struct UintToAddressMap {
        Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
        return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
        return _remove(map._inner, bytes32(key));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
        return _contains(map._inner, bytes32(key));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToAddressMap storage map) internal view returns (uint256) {
        return _length(map._inner);
    }

   /**
    * @dev Returns the element stored at position `index` in the set. O(1).
    * Note that there are no guarantees on the ordering of values inside the
    * array, and it may change when more values are added or removed.
    *
    * Requirements:
    *
    * - `index` must be strictly less than {length}.
    */
    function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
        (bytes32 key, bytes32 value) = _at(map._inner, index);
        return (uint256(key), address(uint160(uint256(value))));
    }

    /**
     * @dev Tries to returns the value associated with `key`.  O(1).
     * Does not revert if `key` is not in the map.
     *
     * _Available since v3.4._
     */
    function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
        (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
        return (success, address(uint160(uint256(value))));
    }

    /**
     * @dev Returns the value associated with `key`.  O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
        return address(uint160(uint256(_get(map._inner, bytes32(key)))));
    }

    /**
     * @dev Same as {get}, with a custom error message when `key` is not in the map.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryGet}.
     */
    function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
        return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
    }
}

// File: @openzeppelin/contracts/utils/Strings.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    /**
     * @dev Converts a `uint256` to its ASCII `string` representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        uint256 index = digits - 1;
        temp = value;
        while (temp != 0) {
            buffer[index--] = bytes1(uint8(48 + temp % 10));
            temp /= 10;
        }
        return string(buffer);
    }
}

// File: @openzeppelin/contracts/token/ERC721/ERC721.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;












/**
 * @title ERC721 Non-Fungible Token Standard basic implementation
 * @dev see https://eips.ethereum.org/EIPS/eip-721
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {
    using SafeMath for uint256;
    using Address for address;
    using EnumerableSet for EnumerableSet.UintSet;
    using EnumerableMap for EnumerableMap.UintToAddressMap;
    using Strings for uint256;

    // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
    // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
    bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;

    // Mapping from holder address to their (enumerable) set of owned tokens
    mapping (address => EnumerableSet.UintSet) private _holderTokens;

    // Enumerable mapping from token ids to their owners
    EnumerableMap.UintToAddressMap private _tokenOwners;

    // Mapping from token ID to approved address
    mapping (uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping (address => mapping (address => bool)) private _operatorApprovals;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Optional mapping for token URIs
    mapping (uint256 => string) private _tokenURIs;

    // Base URI
    string private _baseURI;

    /*
     *     bytes4(keccak256('balanceOf(address)')) == 0x70a08231
     *     bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
     *     bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
     *     bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
     *     bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
     *     bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
     *     bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
     *     bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
     *     bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
     *
     *     => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
     *        0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
     */
    bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;

    /*
     *     bytes4(keccak256('name()')) == 0x06fdde03
     *     bytes4(keccak256('symbol()')) == 0x95d89b41
     *     bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
     *
     *     => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
     */
    bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;

    /*
     *     bytes4(keccak256('totalSupply()')) == 0x18160ddd
     *     bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
     *     bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
     *
     *     => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
     */
    bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor (string memory name_, string memory symbol_) public {
        _name = name_;
        _symbol = symbol_;

        // register the supported interfaces to conform to ERC721 via ERC165
        _registerInterface(_INTERFACE_ID_ERC721);
        _registerInterface(_INTERFACE_ID_ERC721_METADATA);
        _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _holderTokens[owner].length();
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }
        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
        return string(abi.encodePacked(base, tokenId.toString()));
    }

    /**
    * @dev Returns the base URI set via {_setBaseURI}. This will be
    * automatically added as a prefix in {tokenURI} to each token's URI, or
    * to the token ID if no specific URI is set for that token ID.
    */
    function baseURI() public view virtual returns (string memory) {
        return _baseURI;
    }

    /**
     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        return _holderTokens[owner].at(index);
    }

    /**
     * @dev See {IERC721Enumerable-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
        return _tokenOwners.length();
    }

    /**
     * @dev See {IERC721Enumerable-tokenByIndex}.
     */
    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        (uint256 tokenId, ) = _tokenOwners.at(index);
        return tokenId;
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(operator != _msgSender(), "ERC721: approve to caller");

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _tokenOwners.contains(tokenId);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender));
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     d*
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
        _mint(to, tokenId);
        require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _holderTokens[to].add(tokenId);

        _tokenOwners.set(tokenId, to);

        emit Transfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId); // internal owner

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        // Clear metadata (if any)
        if (bytes(_tokenURIs[tokenId]).length != 0) {
            delete _tokenURIs[tokenId];
        }

        _holderTokens[owner].remove(tokenId);

        _tokenOwners.remove(tokenId);

        emit Transfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _holderTokens[from].remove(tokenId);
        _holderTokens[to].add(tokenId);

        _tokenOwners.set(tokenId, to);

        emit Transfer(from, to, tokenId);
    }

    /**
     * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
        require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
        _tokenURIs[tokenId] = _tokenURI;
    }

    /**
     * @dev Internal function to set the base URI for all token IDs. It is
     * automatically added as a prefix to the value returned in {tokenURI},
     * or to the token ID if {tokenURI} is empty.
     */
    function _setBaseURI(string memory baseURI_) internal virtual {
        _baseURI = baseURI_;
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
        private returns (bool)
    {
        if (!to.isContract()) {
            return true;
        }
        bytes memory returndata = to.functionCall(abi.encodeWithSelector(
            IERC721Receiver(to).onERC721Received.selector,
            _msgSender(),
            from,
            tokenId,
            _data
        ), "ERC721: transfer to non ERC721Receiver implementer");
        bytes4 retval = abi.decode(returndata, (bytes4));
        return (retval == _ERC721_RECEIVED);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits an {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}

// File: @openzeppelin/contracts/utils/Counters.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;


/**
 * @title Counters
 * @author Matt Condon (@shrugs)
 * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
 * of elements in a mapping, issuing ERC721 ids, or counting request ids.
 *
 * Include with `using Counters for Counters.Counter;`
 * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath}
 * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
 * directly accessed.
 */
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 {
        // The {SafeMath} overflow check can be skipped here, see the comment at the top
        counter._value += 1;
    }

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

// File: contracts/Achievements.sol

pragma solidity ^0.6.0;




// openzeppelin imports




/// @title Prediction Market Achievements Contract
contract Achievements is ERC721 {
  using Counters for Counters.Counter;
  Counters.Counter private _tokenIds;

  /// @dev protocol is immutable and has no ownership
  RealitioERC20 public realitioERC20;
  PredictionMarketV2 public predictionMarket;
  PredictionMarketResolver public predictionMarketResolver;

  event LogNewAchievement(uint256 indexed achievementId, address indexed user, string content);

  // Buy: Claim through market[x].outcome[y].shares.holders[msg.sender] > 0
  // AddLiquidity: Claim through market[x].liquidityShares[msg.sender] > 0
  // CreateMarket: RealitioERC20 question arbitrator == msg.sender
  // ClaimWinnings: Claim through market[x].outcome[y].shares.holders[msg.sender] > 0 && y == market.resolution.outcomeId
  // Bond: Use RealitioERC20 historyHashes logic value and ensure addrs.includes(msg.sender)
  enum Action {
    Buy,
    AddLiquidity,
    Bond,
    ClaimWinnings,
    CreateMarket
  }

  struct Achievement {
    Action action;
    uint256 occurrences;
    mapping(address => bool) claims;
  }

  uint256 public achievementIndex = 0;
  mapping(uint256 => Achievement) public achievements;
  mapping(uint256 => uint256) public tokens; // tokenId => achievementId

  constructor(string memory token, string memory ticker) public ERC721(token, ticker) {}

  function setContracts(
    PredictionMarketV2 _predictionMarket,
    PredictionMarketResolver _predictionMarketResolver,
    RealitioERC20 _realitioERC20
  ) public {
    require(address(predictionMarket) == address(0), "predictionMarket can only be initialized once");
    require(address(predictionMarketResolver) == address(0), "predictionMarket can only be initialized once");
    require(address(realitioERC20) == address(0), "realitioERC20 can only be initialized once");

    require(address(_predictionMarket) != address(0), "_predictionMarket address is 0");
    require(address(_predictionMarketResolver) != address(0), "_predictionMarketResolver address is 0");
    require(address(_realitioERC20) != address(0), "_realitioERC20 address is 0");

    predictionMarket = _predictionMarket;
    predictionMarketResolver = _predictionMarketResolver;
    realitioERC20 = _realitioERC20;
  }

  function setBaseURI(string memory _baseURI) public {
    require(bytes(baseURI()).length == 0, "baseURI can only be initialized once");

    _setBaseURI(_baseURI);
  }

  function createAchievement(
    Action action,
    uint256 occurrences,
    string memory content
  ) public returns (uint256) {
    require(occurrences > 0, "occurrences has to be greater than 0");
    uint256 achievementId = achievementIndex;
    Achievement storage achievement = achievements[achievementId];

    achievement.action = action;
    achievement.occurrences = occurrences;
    emit LogNewAchievement(achievementId, msg.sender, content);
    achievementIndex = achievementId + 1;
    return achievementId;
  }

  function hasUserClaimedAchievement(address user, uint256 achievementId) public view returns (bool) {
    Achievement storage achievement = achievements[achievementId];

    return achievement.claims[user];
  }

  function hasUserPlacedPrediction(address user, uint256 marketId) public view returns (bool) {
    uint256[] memory outcomeShares;
    (, outcomeShares) = predictionMarket.getUserMarketShares(marketId, user);

    bool hasPlacedPrediction;

    for (uint256 i = 0; i < outcomeShares.length; i++) {
      if (outcomeShares[i] > 0) {
        hasPlacedPrediction = true;
        break;
      }
    }

    require(hasPlacedPrediction == true, "user does not hold outcome shares");

    return true;
  }

  function hasUserAddedLiquidity(address user, uint256 marketId) public view returns (bool) {
    uint256 liquidityShares;
    (liquidityShares, ) = predictionMarket.getUserMarketShares(marketId, user);

    require(liquidityShares > 0, "user does not hold liquidity shares");

    return true;
  }

  function hasUserPlacedBond(
    address user,
    uint256 marketId,
    bytes32[] memory history_hashes,
    address[] memory addrs,
    uint256[] memory bonds,
    bytes32[] memory answers
  ) public view returns (bool) {
    bytes32 last_history_hash;
    bytes32 questionId;
    (, questionId, ) = predictionMarket.getMarketAltData(marketId);
    (, , , , , , , , last_history_hash, ) = realitioERC20.questions(questionId);
    bool bonded;

    uint256 i;
    for (i = 0; i < history_hashes.length; i++) {
      require(
        last_history_hash == keccak256(abi.encodePacked(history_hashes[i], answers[i], bonds[i], addrs[i], false))
      );

      if (addrs[i] == user) bonded = true;

      last_history_hash = history_hashes[i];
    }

    require(bonded == true, "user has not placed a bond in market");

    return true;
  }

  function hasUserClaimedWinnings(address user, uint256 marketId) public view returns (bool) {
    uint256[] memory outcomeShares;
    (, outcomeShares) = predictionMarket.getUserMarketShares(marketId, user);
    PredictionMarketResolver.MarketResolution memory marketResolution = predictionMarketResolver.getMarketResolution(
      marketId
    );

    uint256 resolvedOutcomeId = marketResolution.outcomeId;

    require(marketResolution.resolved == true, "market has not been resolved");
    require(outcomeShares[resolvedOutcomeId] > 0, "user does not hold winning outcome shares");

    return true;
  }

  function hasUserCreatedMarket(address user, uint256 marketId) public view returns (bool) {
    require(user != address(0), "user address is 0x0");

    bytes32 questionId;
    address arbitrator;
    (, questionId, ) = predictionMarket.getMarketAltData(marketId);
    (, arbitrator, , , , , , , , ) = realitioERC20.questions(questionId);

    require(user == arbitrator, "user did not create market");

    return true;
  }

  function claimAchievement(uint256 achievementId, uint256[] memory marketIds) public returns (uint256) {
    Achievement storage achievement = achievements[achievementId];

    require(achievement.claims[msg.sender] == false, "Achievement already claimed");
    require(achievement.action != Action.Bond, "Method not used for bond placement achievements");
    require(marketIds.length == achievement.occurrences, "Markets count and occurrences don't match");

    for (uint256 i = 0; i < marketIds.length; i++) {
      uint256 marketId = marketIds[i];

      if (achievement.action == Action.Buy) {
        hasUserPlacedPrediction(msg.sender, marketId);
      } else if (achievement.action == Action.AddLiquidity) {
        hasUserAddedLiquidity(msg.sender, marketId);
      } else if (achievement.action == Action.ClaimWinnings) {
        hasUserClaimedWinnings(msg.sender, marketId);
      } else if (achievement.action == Action.CreateMarket) {
        hasUserCreatedMarket(msg.sender, marketId);
      } else {
        revert("Invalid achievement action");
      }
    }

    mintAchievement(msg.sender, achievementId);
  }

  function claimAchievement(
    uint256 achievementId,
    uint256[] memory marketIds,
    uint256[] memory lengths,
    bytes32[] memory history_hashes,
    address[] memory addrs,
    uint256[] memory bonds,
    bytes32[] memory answers
  ) public {
    Achievement storage achievement = achievements[achievementId];

    require(achievement.claims[msg.sender] == false, "Achievement already claimed");
    require(achievement.action == Action.Bond, "Method only used for bond placement achievements");
    require(marketIds.length > 0, "No actions provided");
    require(marketIds.length == achievement.occurrences, "Markets count and occurrences don't match");

    uint256 qi;

    for (uint256 i = 0; i < marketIds.length; i++) {
      uint256 marketId = marketIds[i];

      uint256 ln = lengths[i];
      bytes32[] memory hh = new bytes32[](ln);
      address[] memory ad = new address[](ln);
      uint256[] memory bo = new uint256[](ln);
      bytes32[] memory an = new bytes32[](ln);
      uint256 j;
      for (j = 0; j < ln; j++) {
        hh[j] = history_hashes[qi];
        ad[j] = addrs[qi];
        bo[j] = bonds[qi];
        an[j] = answers[qi];
        qi++;
      }
      hasUserPlacedBond(msg.sender, marketId, hh, ad, bo, an);
    }

    mintAchievement(msg.sender, achievementId);
  }

  function mintAchievement(address user, uint256 achievementId) private returns (uint256) {
    _tokenIds.increment();

    uint256 tokenId = _tokenIds.current();
    _mint(user, tokenId);
    tokens[tokenId] = achievementId;

    Achievement storage achievement = achievements[achievementId];
    achievement.claims[user] = true;

    return tokenId;
  }

  function tokenIndex() public view returns (uint256) {
    return _tokenIds.current();
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"string","name":"token","type":"string"},{"internalType":"string","name":"ticker","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"achievementId","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"string","name":"content","type":"string"}],"name":"LogNewAchievement","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"achievementIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"achievements","outputs":[{"internalType":"enum Achievements.Action","name":"action","type":"uint8"},{"internalType":"uint256","name":"occurrences","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"achievementId","type":"uint256"},{"internalType":"uint256[]","name":"marketIds","type":"uint256[]"}],"name":"claimAchievement","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"achievementId","type":"uint256"},{"internalType":"uint256[]","name":"marketIds","type":"uint256[]"},{"internalType":"uint256[]","name":"lengths","type":"uint256[]"},{"internalType":"bytes32[]","name":"history_hashes","type":"bytes32[]"},{"internalType":"address[]","name":"addrs","type":"address[]"},{"internalType":"uint256[]","name":"bonds","type":"uint256[]"},{"internalType":"bytes32[]","name":"answers","type":"bytes32[]"}],"name":"claimAchievement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum Achievements.Action","name":"action","type":"uint8"},{"internalType":"uint256","name":"occurrences","type":"uint256"},{"internalType":"string","name":"content","type":"string"}],"name":"createAchievement","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"hasUserAddedLiquidity","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"achievementId","type":"uint256"}],"name":"hasUserClaimedAchievement","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"hasUserClaimedWinnings","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"hasUserCreatedMarket","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"},{"internalType":"bytes32[]","name":"history_hashes","type":"bytes32[]"},{"internalType":"address[]","name":"addrs","type":"address[]"},{"internalType":"uint256[]","name":"bonds","type":"uint256[]"},{"internalType":"bytes32[]","name":"answers","type":"bytes32[]"}],"name":"hasUserPlacedBond","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"hasUserPlacedPrediction","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"predictionMarket","outputs":[{"internalType":"contract PredictionMarketV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"predictionMarketResolver","outputs":[{"internalType":"contract PredictionMarketResolver","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"realitioERC20","outputs":[{"internalType":"contract RealitioERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract PredictionMarketV2","name":"_predictionMarket","type":"address"},{"internalType":"contract PredictionMarketResolver","name":"_predictionMarketResolver","type":"address"},{"internalType":"contract RealitioERC20","name":"_realitioERC20","type":"address"}],"name":"setContracts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040526000600e553480156200001657600080fd5b506040516200389438038062003894833981016040819052620000399162000284565b8181620000566301ffc9a760e01b6001600160e01b03620000dd16565b81516200006b90600690602085019062000138565b5080516200008190600790602084019062000138565b506200009d6380ac58cd60e01b6001600160e01b03620000dd16565b620000b8635b5e139f60e01b6001600160e01b03620000dd16565b620000d363780e9d6360e01b6001600160e01b03620000dd16565b5050505062000322565b6001600160e01b03198082161415620001135760405162461bcd60e51b81526004016200010a90620002eb565b60405180910390fd5b6001600160e01b0319166000908152602081905260409020805460ff19166001179055565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200017b57805160ff1916838001178555620001ab565b82800160010185558215620001ab579182015b82811115620001ab5782518255916020019190600101906200018e565b50620001b9929150620001bd565b5090565b620001da91905b80821115620001b95760008155600101620001c4565b90565b600082601f830112620001ee578081fd5b81516001600160401b038082111562000205578283fd5b6040516020601f8401601f191682018101838111838210171562000227578586fd5b806040525081945083825286818588010111156200024457600080fd5b600092505b8383101562000268578583018101518284018201529182019162000249565b838311156200027a5760008185840101525b5050505092915050565b6000806040838503121562000297578182fd5b82516001600160401b0380821115620002ae578384fd5b620002bc86838701620001dd565b93506020850151915080821115620002d2578283fd5b50620002e185828601620001dd565b9150509250929050565b6020808252601c908201527f4552433136353a20696e76616c696420696e7465726661636520696400000000604082015260600190565b61356280620003326000396000f3fe608060405234801561001057600080fd5b50600436106101a15760003560e01c806301ffc9a7146101a657806306fdde03146101cf578063081812fc146101e4578063095ea7b3146102045780630c04f83b1461021957806318160ddd1461022c57806323b872dd1461024157806327aed72e146102545780632f745c591461027557806342842e0e1461028857806342da0beb1461029b578063497e4096146102ae5780634f64b2be146102c15780634f6ccce7146102d457806355f804b3146102e75780636352211e146102fa5780636be5179f1461030d5780636c0360eb146103205780636f64432a1461032857806370a08231146103305780638c8b8f4e14610343578063947f53a21461035657806395d89b4114610369578063a22cb46514610371578063b3066d4914610384578063b88d4fde14610397578063c87b56dd146103aa578063c8c787ae146103bd578063c97c8976146103d0578063c99aea33146103d8578063d55f9273146103e0578063def114b6146103e8578063e7c38730146103f0578063e985e9c514610403578063ecf5f69b14610416575b600080fd5b6101b96101b43660046125ef565b610429565b6040516101c69190612a55565b60405180910390f35b6101d761044c565b6040516101c69190612a81565b6101f76101f2366004612737565b6104e3565b6040516101c69190612a04565b61021761021236600461245d565b61052f565b005b6101b961022736600461245d565b6105c7565b6102346106c1565b6040516101c69190612a60565b61021761024f366004612387565b6106d2565b610267610262366004612737565b61070a565b6040516101c6929190612a69565b61023461028336600461245d565b610729565b610217610296366004612387565b610758565b6101b96102a936600461245d565b610773565b6102346102bc36600461274f565b6107a1565b6102346102cf366004612737565b610924565b6102346102e2366004612737565b610936565b6102176102f53660046126ca565b610952565b6101f7610308366004612737565b610984565b61021761031b366004612833565b6109b2565b6101d7610c71565b6101f7610cd2565b61023461033e366004612333565b610ce1565b6101b9610351366004612488565b610d2a565b6101b961036436600461245d565b610f5a565b6101d76110dc565b61021761037f366004612430565b61113d565b610217610392366004612627565b61120b565b6102176103a53660046123c7565b611337565b6101d76103b8366004612737565b611376565b6102346103cb366004612671565b6114bc565b6101f761155e565b61023461156d565b610234611573565b6101f761157f565b6101b96103fe36600461245d565b61158e565b6101b961041136600461234f565b611643565b6101b961042436600461245d565b611671565b6001600160e01b0319811660009081526020819052604090205460ff165b919050565b60068054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104d85780601f106104ad576101008083540402835291602001916104d8565b820191906000526020600020905b8154815290600101906020018083116104bb57829003601f168201915b505050505090505b90565b60006104ee826117db565b6105135760405162461bcd60e51b815260040161050a90612f89565b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b600061053a82610984565b9050806001600160a01b0316836001600160a01b0316141561056e5760405162461bcd60e51b815260040161050a9061314d565b806001600160a01b03166105806117ee565b6001600160a01b0316148061059c575061059c816104116117ee565b6105b85760405162461bcd60e51b815260040161050a90612d70565b6105c283836117f2565b505050565b600c5460405163b31eb89560e01b81526000916060916001600160a01b039091169063b31eb895906105ff90869088906004016133de565b60006040518083038186803b15801561061757600080fd5b505afa15801561062b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106539190810190612793565b915060009050805b825181101561069157600083828151811061067257fe5b602002602001015111156106895760019150610691565b60010161065b565b506001811515146106b45760405162461bcd60e51b815260040161050a90612ba7565b6001925050505b92915050565b60006106cd6002611860565b905090565b6106e36106dd6117ee565b8261186b565b6106ff5760405162461bcd60e51b815260040161050a90613201565b6105c28383836118f0565b600f602052600090815260409020805460019091015460ff9091169082565b6001600160a01b0382166000908152600160205260408120610751908363ffffffff6119fe16565b9392505050565b6105c283838360405180602001604052806000815250611337565b6000908152600f602090815260408083206001600160a01b0394909416835260029093019052205460ff1690565b6000828152600f602090815260408083203384526002810190925282205460ff16156107df5760405162461bcd60e51b815260040161050a90613252565b6002815460ff1660048111156107f157fe5b141561080f5760405162461bcd60e51b815260040161050a9061334b565b80600101548351146108335760405162461bcd60e51b815260040161050a90612b28565b60005b835181101561091157600084828151811061084d57fe5b602002602001015190506000600481111561086457fe5b835460ff16600481111561087457fe5b141561088a5761088433826105c7565b50610908565b6001835460ff16600481111561089c57fe5b14156108ac57610884338261158e565b6003835460ff1660048111156108be57fe5b14156108ce576108843382610f5a565b6004835460ff1660048111156108e057fe5b14156108f0576108843382611671565b60405162461bcd60e51b815260040161050a906130d6565b50600101610836565b5061091c3385611a0a565b505092915050565b60106020526000908152604090205481565b60008061094a60028463ffffffff611a7316565b509392505050565b61095a610c71565b51156109785760405162461bcd60e51b815260040161050a90612c5f565b61098181611a8f565b50565b60006106bb826040518060600160405280602981526020016134e4602991396002919063ffffffff611aa616565b6000878152600f60209081526040808320338452600281019092529091205460ff16156109f15760405162461bcd60e51b815260040161050a90613252565b6002815460ff166004811115610a0357fe5b14610a205760405162461bcd60e51b815260040161050a90612f39565b6000875111610a415760405162461bcd60e51b815260040161050a90612e12565b8060010154875114610a655760405162461bcd60e51b815260040161050a90612b28565b6000805b8851811015610c5a576000898281518110610a8057fe5b602002602001015190506000898381518110610a9857fe5b60200260200101519050606081604051908082528060200260200182016040528015610ace578160200160208202803883390190505b509050606082604051908082528060200260200182016040528015610afd578160200160208202803883390190505b509050606083604051908082528060200260200182016040528015610b2c578160200160208202803883390190505b509050606084604051908082528060200260200182016040528015610b5b578160200160208202803883390190505b50905060005b85811015610c38578d8981518110610b7557fe5b6020026020010151858281518110610b8957fe5b6020026020010181815250508c8981518110610ba157fe5b6020026020010151848281518110610bb557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508b8981518110610be157fe5b6020026020010151838281518110610bf557fe5b6020026020010181815250508a8981518110610c0d57fe5b6020026020010151828281518110610c2157fe5b602090810291909101015260019889019801610b61565b610c46338887878787610d2a565b505060019096019550610a69945050505050565b50610c65338a611a0a565b50505050505050505050565b60098054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104d85780601f106104ad576101008083540402835291602001916104d8565b600d546001600160a01b031681565b60006001600160a01b038216610d095760405162461bcd60e51b815260040161050a90612dc8565b6001600160a01b03821660009081526001602052604090206106bb90611860565b600c54604051630ebc905560e11b8152600091829182916001600160a01b031690631d7920aa90610d5f908b90600401612a60565b60606040518083038186803b158015610d7757600080fd5b505afa158015610d8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610daf919081019061292b565b50600b5460405163095addb960e41b81529193506001600160a01b031691506395addb9090610de2908490600401612a60565b6101406040518083038186803b158015610dfb57600080fd5b505afa158015610e0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e339190810190612549565b509950600097508796505050505050505b8851811015610f2757888181518110610e5957fe5b6020026020010151868281518110610e6d57fe5b6020026020010151888381518110610e8157fe5b60200260200101518a8481518110610e9557fe5b60200260200101516000604051602001610eb3959493929190612984565b604051602081830303815290604052805190602001208414610ed457600080fd5b8a6001600160a01b0316888281518110610eea57fe5b60200260200101516001600160a01b03161415610f0657600191505b888181518110610f1257fe5b60209081029190910101519350600101610e44565b600182151514610f495760405162461bcd60e51b815260040161050a906132be565b5060019a9950505050505050505050565b600c5460405163b31eb89560e01b81526000916060916001600160a01b039091169063b31eb89590610f9290869088906004016133de565b60006040518083038186803b158015610faa57600080fd5b505afa158015610fbe573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610fe69190810190612793565b9150610ff2905061213c565b600d5460405163232e45d760e01b81526001600160a01b039091169063232e45d790611022908790600401612a60565b604080518083038186803b15801561103957600080fd5b505afa15801561104d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061107191908101906126fc565b6020810151815191925090151560011461109d5760405162461bcd60e51b815260040161050a90612eb6565b60008382815181106110ab57fe5b6020026020010151116110d05760405162461bcd60e51b815260040161050a90613302565b50600195945050505050565b60078054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104d85780601f106104ad576101008083540402835291602001916104d8565b6111456117ee565b6001600160a01b0316826001600160a01b031614156111765760405162461bcd60e51b815260040161050a90612c2c565b80600560006111836117ee565b6001600160a01b03908116825260208083019390935260409182016000908120918716808252919093529120805460ff1916921515929092179091556111c76117ee565b6001600160a01b03167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31836040516111ff9190612a55565b60405180910390a35050565b600c546001600160a01b0316156112345760405162461bcd60e51b815260040161050a90612eec565b600d546001600160a01b03161561125d5760405162461bcd60e51b815260040161050a90612eec565b600b546001600160a01b0316156112865760405162461bcd60e51b815260040161050a90612d26565b6001600160a01b0383166112ac5760405162461bcd60e51b815260040161050a90612cef565b6001600160a01b0382166112d25760405162461bcd60e51b815260040161050a9061318e565b6001600160a01b0381166112f85760405162461bcd60e51b815260040161050a906130a1565b600c80546001600160a01b039485166001600160a01b031991821617909155600d805493851693821693909317909255600b8054919093169116179055565b6113486113426117ee565b8361186b565b6113645760405162461bcd60e51b815260040161050a90613201565b61137084848484611ab3565b50505050565b6060611381826117db565b61139d5760405162461bcd60e51b815260040161050a9061301e565b60008281526008602090815260409182902080548351601f60026000196101006001861615020190931692909204918201849004840281018401909452808452606093928301828280156114325780601f1061140757610100808354040283529160200191611432565b820191906000526020600020905b81548152906001019060200180831161141557829003601f168201915b505050505090506060611443610c71565b905080516000141561145757509050610447565b8151156114895780826040516020016114719291906129d6565b60405160208183030381529060405292505050610447565b8061149385611ae6565b6040516020016114a49291906129d6565b60405160208183030381529060405292505050919050565b60008083116114dd5760405162461bcd60e51b815260040161050a9061339a565b600e546000818152600f6020526040902080548690829060ff1916600183600481111561150657fe5b021790555060018101859055604051339083907ff12dd2cec284c7695fa5ff87a683b64add6d47d7a74e190171798ee5e70a744c90611546908890612a81565b60405180910390a35060018101600e55949350505050565b600b546001600160a01b031681565b600e5481565b60006106cd600a611baa565b600c546001600160a01b031681565b600c5460405163b31eb89560e01b815260009182916001600160a01b039091169063b31eb895906115c590869088906004016133de565b60006040518083038186803b1580156115dd57600080fd5b505afa1580156115f1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526116199190810190612793565b509050806116395760405162461bcd60e51b815260040161050a9061310a565b5060019392505050565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b60006001600160a01b0383166116995760405162461bcd60e51b815260040161050a906131d4565b600c54604051630ebc905560e11b815260009182916001600160a01b0390911690631d7920aa906116ce908790600401612a60565b60606040518083038186803b1580156116e657600080fd5b505afa1580156116fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171e919081019061292b565b50600b5460405163095addb960e41b81529194506001600160a01b031691506395addb9090611751908590600401612a60565b6101406040518083038186803b15801561176a57600080fd5b505afa15801561177e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117a29190810190612549565b509698505050506001600160a01b038a81169087161494506106b493505050505760405162461bcd60e51b815260040161050a9061306d565b60006106bb60028363ffffffff611bae16565b3390565b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061182782610984565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60006106bb82611baa565b6000611876826117db565b6118925760405162461bcd60e51b815260040161050a90612ca3565b600061189d83610984565b9050806001600160a01b0316846001600160a01b031614806118d85750836001600160a01b03166118cd846104e3565b6001600160a01b0316145b806118e857506118e88185611643565b949350505050565b826001600160a01b031661190382610984565b6001600160a01b0316146119295760405162461bcd60e51b815260040161050a90612fd5565b6001600160a01b03821661194f5760405162461bcd60e51b815260040161050a90612be8565b61195a8383836105c2565b6119656000826117f2565b6001600160a01b038316600090815260016020526040902061198d908263ffffffff611bba16565b506001600160a01b03821660009081526001602052604090206119b6908263ffffffff611bc616565b506119c96002828463ffffffff611bd216565b5080826001600160a01b0316846001600160a01b031660008051602061350d83398151915260405160405180910390a4505050565b60006107518383611be8565b6000611a16600a611c2d565b6000611a22600a611baa565b9050611a2e8482611c36565b6000818152601060209081526040808320869055858352600f82528083206001600160a01b03881684526002019091529020805460ff19166001179055905092915050565b6000808080611a828686611cf4565b9097909650945050505050565b8051611aa2906009906020840190612153565b5050565b60006118e8848484611d50565b611abe8484846118f0565b611aca84848484611daf565b6113705760405162461bcd60e51b815260040161050a90612ad6565b606081611b0b57506040805180820190915260018152600360fc1b6020820152610447565b8160005b8115611b2357600101600a82049150611b0f565b6060816040519080825280601f01601f191660200182016040528015611b50576020820181803883390190505b50859350905060001982015b8315611ba157600a840660300160f81b82828060019003935081518110611b7f57fe5b60200101906001600160f81b031916908160001a905350600a84049350611b5c565b50949350505050565b5490565b60006107518383611e94565b60006107518383611eac565b60006107518383611f72565b60006118e884846001600160a01b038516611fbc565b81546000908210611c0b5760405162461bcd60e51b815260040161050a90612a94565b826000018281548110611c1a57fe5b9060005260206000200154905092915050565b80546001019055565b6001600160a01b038216611c5c5760405162461bcd60e51b815260040161050a90612e81565b611c65816117db565b15611c825760405162461bcd60e51b815260040161050a90612b71565b611c8e600083836105c2565b6001600160a01b0382166000908152600160205260409020611cb6908263ffffffff611bc616565b50611cc96002828463ffffffff611bd216565b5060405181906001600160a01b0384169060009060008051602061350d833981519152908290a45050565b815460009081908310611d195760405162461bcd60e51b815260040161050a90612e3f565b6000846000018481548110611d2a57fe5b906000526020600020906002020190508060000154816001015492509250509250929050565b60008281526001840160205260408120548281611d805760405162461bcd60e51b815260040161050a9190612a81565b50846000016001820381548110611d9357fe5b9060005260206000209060020201600101549150509392505050565b6000611dc3846001600160a01b0316612053565b611dcf575060016118e8565b6060611e5d630a85bd0160e11b611de46117ee565b888787604051602401611dfa9493929190612a18565b604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518060600160405280603281526020016134b2603291396001600160a01b038816919063ffffffff61205916565b9050600081806020019051611e75919081019061260b565b6001600160e01b031916630a85bd0160e11b1492505050949350505050565b60009081526001919091016020526040902054151590565b60008181526001830160205260408120548015611f685783546000198083019190810190600090879083908110611edf57fe5b9060005260206000200154905080876000018481548110611efc57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080611f2c57fe5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506106bb565b60009150506106bb565b6000611f7e8383611e94565b611fb4575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556106bb565b5060006106bb565b600082815260018401602052604081205480612021575050604080518082018252838152602080820184815286546001818101895560008981528481209551600290930290950191825591519082015586548684528188019092529290912055610751565b8285600001600183038154811061203457fe5b9060005260206000209060020201600101819055506000915050610751565b3b151590565b60606118e884846000858561206d85612053565b6120895760405162461bcd60e51b815260040161050a90613287565b60006060866001600160a01b031685876040516120a691906129ba565b60006040518083038185875af1925050503d80600081146120e3576040519150601f19603f3d011682016040523d82523d6000602084013e6120e8565b606091505b50915091506120f8828286612103565b979650505050505050565b60608315612112575081610751565b8251156121225782518084602001fd5b8160405162461bcd60e51b815260040161050a9190612a81565b604080518082019091526000808252602082015290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061219457805160ff19168380011785556121c1565b828001600101855582156121c1579182015b828111156121c15782518255916020019190600101906121a6565b506121cd9291506121d1565b5090565b6104e091905b808211156121cd57600081556001016121d7565b80356106bb81613466565b600082601f830112612206578081fd5b81356122196122148261341b565b6133f5565b81815291506020808301908481018184028601820187101561223a57600080fd5b60005b8481101561226257813561225081613466565b8452928201929082019060010161223d565b505050505092915050565b600082601f83011261227d578081fd5b813561228b6122148261341b565b8181529150602080830190848101818402860182018710156122ac57600080fd5b60005b84811015612262578135845292820192908201906001016122af565b600082601f8301126122db578081fd5b81356001600160401b038111156122f0578182fd5b612303601f8201601f19166020016133f5565b915080825283602082850101111561231a57600080fd5b8060208401602084013760009082016020015292915050565b600060208284031215612344578081fd5b813561075181613466565b60008060408385031215612361578081fd5b823561236c81613466565b9150602083013561237c81613466565b809150509250929050565b60008060006060848603121561239b578081fd5b83356123a681613466565b925060208401356123b681613466565b929592945050506040919091013590565b600080600080608085870312156123dc578182fd5b84356123e781613466565b935060208501356123f781613466565b92506040850135915060608501356001600160401b03811115612418578182fd5b612424878288016122cb565b91505092959194509250565b60008060408385031215612442578182fd5b823561244d81613466565b9150602083013561237c8161347b565b6000806040838503121561246f578182fd5b823561247a81613466565b946020939093013593505050565b60008060008060008060c087890312156124a0578384fd5b6124aa88886121eb565b95506020870135945060408701356001600160401b03808211156124cc578586fd5b6124d88a838b0161226d565b955060608901359150808211156124ed578384fd5b6124f98a838b016121f6565b9450608089013591508082111561250e578384fd5b61251a8a838b0161226d565b935060a089013591508082111561252f578283fd5b5061253c89828a0161226d565b9150509295509295509295565b6000806000806000806000806000806101408b8d031215612568578788fd5b8a51995060208b015161257a81613466565b60408c015190995061258b8161349f565b60608c015190985061259c8161349f565b60808c01519097506125ad8161349f565b60a08c01519096506125be8161347b565b8095505060c08b0151935060e08b015192506101008b015191506101208b015190509295989b9194979a5092959850565b600060208284031215612600578081fd5b813561075181613489565b60006020828403121561261c578081fd5b815161075181613489565b60008060006060848603121561263b578081fd5b833561264681613466565b9250602084013561265681613466565b9150604084013561266681613466565b809150509250925092565b600080600060608486031215612685578081fd5b833560058110612693578182fd5b92506020840135915060408401356001600160401b038111156126b4578182fd5b6126c0868287016122cb565b9150509250925092565b6000602082840312156126db578081fd5b81356001600160401b038111156126f0578182fd5b6118e8848285016122cb565b60006040828403121561270d578081fd5b61271760406133f5565b82516127228161347b565b81526020928301519281019290925250919050565b600060208284031215612748578081fd5b5035919050565b60008060408385031215612761578182fd5b8235915060208301356001600160401b0381111561277d578182fd5b6127898582860161226d565b9150509250929050565b600080604083850312156127a5578182fd5b8251602080850151919350906001600160401b038111156127c4578283fd5b80850186601f8201126127d5578384fd5b805191506127e56122148361341b565b82815283810190828501858502840186018a1015612801578687fd5b8693505b84841015612823578051835260019390930192918501918501612805565b5080955050505050509250929050565b600080600080600080600060e0888a03121561284d578081fd5b8735965060208801356001600160401b038082111561286a578283fd5b6128768b838c0161226d565b975060408a013591508082111561288b578283fd5b6128978b838c0161226d565b965060608a01359150808211156128ac578283fd5b6128b88b838c0161226d565b955060808a01359150808211156128cd578283fd5b6128d98b838c016121f6565b945060a08a01359150808211156128ee578283fd5b6128fa8b838c0161226d565b935060c08a013591508082111561290f578283fd5b5061291c8a828b0161226d565b91505092959891949750929550565b60008060006060848603121561293f578081fd5b8351925060208401519150604084015190509250925092565b6000815180845261297081602086016020860161343a565b601f01601f19169290920160200192915050565b94855260208501939093526040840191909152606090811b6001600160601b03191690830152151560f81b607482015260750190565b600082516129cc81846020870161343a565b9190910192915050565b600083516129e881846020880161343a565b83519083016129fb82826020880161343a565b01949350505050565b6001600160a01b0391909116815260200190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612a4b90830184612958565b9695505050505050565b901515815260200190565b90815260200190565b6040810160058410612a7757fe5b9281526020015290565b6000602082526107516020830184612958565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60208082526029908201527f4d61726b65747320636f756e7420616e64206f6363757272656e63657320646f6040820152680dc4ee840dac2e8c6d60bb1b606082015260800190565b6020808252601c908201527b115490cdcc8c4e881d1bdad95b88185b1c9958591e481b5a5b9d195960221b604082015260600190565b60208082526021908201527f7573657220646f6573206e6f7420686f6c64206f7574636f6d652073686172656040820152607360f81b606082015260800190565b60208082526024908201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646040820152637265737360e01b606082015260800190565b60208082526019908201527822a9219b99189d1030b8383937bb32903a379031b0b63632b960391b604082015260600190565b60208082526024908201527f626173655552492063616e206f6e6c7920626520696e697469616c697a6564206040820152636f6e636560e01b606082015260800190565b6020808252602c908201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860408201526b34b9ba32b73a103a37b5b2b760a11b606082015260800190565b6020808252601e908201527f5f70726564696374696f6e4d61726b6574206164647265737320697320300000604082015260600190565b6020808252602a908201527f7265616c6974696f45524332302063616e206f6e6c7920626520696e697469616040820152696c697a6564206f6e636560b01b606082015260800190565b60208082526038908201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f776040820152771b995c881b9bdc88185c1c1c9bdd995908199bdc88185b1b60421b606082015260800190565b6020808252602a908201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604082015269726f206164647265737360b01b606082015260800190565b602080825260139082015272139bc81858dd1a5bdb9cc81c1c9bdd9a591959606a1b604082015260600190565b60208082526022908201527f456e756d657261626c654d61703a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252818101527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604082015260600190565b6020808252601c908201527b1b585c9ad95d081a185cc81b9bdd081899595b881c995cdbdb1d995960221b604082015260600190565b6020808252602d908201527f70726564696374696f6e4d61726b65742063616e206f6e6c7920626520696e6960408201526c7469616c697a6564206f6e636560981b606082015260800190565b60208082526030908201527f4d6574686f64206f6e6c79207573656420666f7220626f6e6420706c6163656d60408201526f656e7420616368696576656d656e747360801b606082015260800190565b6020808252602c908201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860408201526b34b9ba32b73a103a37b5b2b760a11b606082015260800190565b60208082526029908201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960408201526839903737ba1037bbb760b91b606082015260800190565b6020808252602f908201527f4552433732314d657461646174613a2055524920717565727920666f72206e6f60408201526e3732bc34b9ba32b73a103a37b5b2b760891b606082015260800190565b6020808252601a90820152791d5cd95c88191a59081b9bdd0818dc99585d19481b585c9ad95d60321b604082015260600190565b6020808252601b908201527a05f7265616c6974696f45524332302061646472657373206973203602c1b604082015260600190565b6020808252601a908201527924b73b30b634b21030b1b434b2bb32b6b2b73a1030b1ba34b7b760311b604082015260600190565b60208082526023908201527f7573657220646f6573206e6f7420686f6c64206c69717569646974792073686160408201526272657360e81b606082015260800190565b60208082526021908201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656040820152603960f91b606082015260800190565b60208082526026908201527f5f70726564696374696f6e4d61726b65745265736f6c7665722061646472657360408201526507320697320360d41b606082015260800190565b60208082526013908201527207573657220616464726573732069732030783606c1b604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b6020808252601b908201527a1058da1a595d995b595b9d08185b1c9958591e4818db185a5b5959602a1b604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b60208082526024908201527f7573657220686173206e6f7420706c61636564206120626f6e6420696e206d616040820152631c9ad95d60e21b606082015260800190565b60208082526029908201527f7573657220646f6573206e6f7420686f6c642077696e6e696e67206f7574636f6040820152686d652073686172657360b81b606082015260800190565b6020808252602f908201527f4d6574686f64206e6f74207573656420666f7220626f6e6420706c6163656d6560408201526e6e7420616368696576656d656e747360881b606082015260800190565b60208082526024908201527f6f6363757272656e6365732068617320746f20626520677265617465722074686040820152630616e20360e41b606082015260800190565b9182526001600160a01b0316602082015260400190565b6040518181016001600160401b038111828210171561341357600080fd5b604052919050565b60006001600160401b03821115613430578081fd5b5060209081020190565b60005b8381101561345557818101518382015260200161343d565b838111156113705750506000910152565b6001600160a01b038116811461098157600080fd5b801515811461098157600080fd5b6001600160e01b03198116811461098157600080fd5b63ffffffff8116811461098157600080fdfe4552433732313a207472616e7366657220746f206e6f6e20455243373231526563656976657220696d706c656d656e7465724552433732313a206f776e657220717565727920666f72206e6f6e6578697374656e7420746f6b656eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220e731d2d277243cb26ce67dd65f8de00b43380526a9a0457ad6a480b5a9a8b17364736f6c6343000602003300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a49464c205469746c657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000449464c5400000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101a15760003560e01c806301ffc9a7146101a657806306fdde03146101cf578063081812fc146101e4578063095ea7b3146102045780630c04f83b1461021957806318160ddd1461022c57806323b872dd1461024157806327aed72e146102545780632f745c591461027557806342842e0e1461028857806342da0beb1461029b578063497e4096146102ae5780634f64b2be146102c15780634f6ccce7146102d457806355f804b3146102e75780636352211e146102fa5780636be5179f1461030d5780636c0360eb146103205780636f64432a1461032857806370a08231146103305780638c8b8f4e14610343578063947f53a21461035657806395d89b4114610369578063a22cb46514610371578063b3066d4914610384578063b88d4fde14610397578063c87b56dd146103aa578063c8c787ae146103bd578063c97c8976146103d0578063c99aea33146103d8578063d55f9273146103e0578063def114b6146103e8578063e7c38730146103f0578063e985e9c514610403578063ecf5f69b14610416575b600080fd5b6101b96101b43660046125ef565b610429565b6040516101c69190612a55565b60405180910390f35b6101d761044c565b6040516101c69190612a81565b6101f76101f2366004612737565b6104e3565b6040516101c69190612a04565b61021761021236600461245d565b61052f565b005b6101b961022736600461245d565b6105c7565b6102346106c1565b6040516101c69190612a60565b61021761024f366004612387565b6106d2565b610267610262366004612737565b61070a565b6040516101c6929190612a69565b61023461028336600461245d565b610729565b610217610296366004612387565b610758565b6101b96102a936600461245d565b610773565b6102346102bc36600461274f565b6107a1565b6102346102cf366004612737565b610924565b6102346102e2366004612737565b610936565b6102176102f53660046126ca565b610952565b6101f7610308366004612737565b610984565b61021761031b366004612833565b6109b2565b6101d7610c71565b6101f7610cd2565b61023461033e366004612333565b610ce1565b6101b9610351366004612488565b610d2a565b6101b961036436600461245d565b610f5a565b6101d76110dc565b61021761037f366004612430565b61113d565b610217610392366004612627565b61120b565b6102176103a53660046123c7565b611337565b6101d76103b8366004612737565b611376565b6102346103cb366004612671565b6114bc565b6101f761155e565b61023461156d565b610234611573565b6101f761157f565b6101b96103fe36600461245d565b61158e565b6101b961041136600461234f565b611643565b6101b961042436600461245d565b611671565b6001600160e01b0319811660009081526020819052604090205460ff165b919050565b60068054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104d85780601f106104ad576101008083540402835291602001916104d8565b820191906000526020600020905b8154815290600101906020018083116104bb57829003601f168201915b505050505090505b90565b60006104ee826117db565b6105135760405162461bcd60e51b815260040161050a90612f89565b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b600061053a82610984565b9050806001600160a01b0316836001600160a01b0316141561056e5760405162461bcd60e51b815260040161050a9061314d565b806001600160a01b03166105806117ee565b6001600160a01b0316148061059c575061059c816104116117ee565b6105b85760405162461bcd60e51b815260040161050a90612d70565b6105c283836117f2565b505050565b600c5460405163b31eb89560e01b81526000916060916001600160a01b039091169063b31eb895906105ff90869088906004016133de565b60006040518083038186803b15801561061757600080fd5b505afa15801561062b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526106539190810190612793565b915060009050805b825181101561069157600083828151811061067257fe5b602002602001015111156106895760019150610691565b60010161065b565b506001811515146106b45760405162461bcd60e51b815260040161050a90612ba7565b6001925050505b92915050565b60006106cd6002611860565b905090565b6106e36106dd6117ee565b8261186b565b6106ff5760405162461bcd60e51b815260040161050a90613201565b6105c28383836118f0565b600f602052600090815260409020805460019091015460ff9091169082565b6001600160a01b0382166000908152600160205260408120610751908363ffffffff6119fe16565b9392505050565b6105c283838360405180602001604052806000815250611337565b6000908152600f602090815260408083206001600160a01b0394909416835260029093019052205460ff1690565b6000828152600f602090815260408083203384526002810190925282205460ff16156107df5760405162461bcd60e51b815260040161050a90613252565b6002815460ff1660048111156107f157fe5b141561080f5760405162461bcd60e51b815260040161050a9061334b565b80600101548351146108335760405162461bcd60e51b815260040161050a90612b28565b60005b835181101561091157600084828151811061084d57fe5b602002602001015190506000600481111561086457fe5b835460ff16600481111561087457fe5b141561088a5761088433826105c7565b50610908565b6001835460ff16600481111561089c57fe5b14156108ac57610884338261158e565b6003835460ff1660048111156108be57fe5b14156108ce576108843382610f5a565b6004835460ff1660048111156108e057fe5b14156108f0576108843382611671565b60405162461bcd60e51b815260040161050a906130d6565b50600101610836565b5061091c3385611a0a565b505092915050565b60106020526000908152604090205481565b60008061094a60028463ffffffff611a7316565b509392505050565b61095a610c71565b51156109785760405162461bcd60e51b815260040161050a90612c5f565b61098181611a8f565b50565b60006106bb826040518060600160405280602981526020016134e4602991396002919063ffffffff611aa616565b6000878152600f60209081526040808320338452600281019092529091205460ff16156109f15760405162461bcd60e51b815260040161050a90613252565b6002815460ff166004811115610a0357fe5b14610a205760405162461bcd60e51b815260040161050a90612f39565b6000875111610a415760405162461bcd60e51b815260040161050a90612e12565b8060010154875114610a655760405162461bcd60e51b815260040161050a90612b28565b6000805b8851811015610c5a576000898281518110610a8057fe5b602002602001015190506000898381518110610a9857fe5b60200260200101519050606081604051908082528060200260200182016040528015610ace578160200160208202803883390190505b509050606082604051908082528060200260200182016040528015610afd578160200160208202803883390190505b509050606083604051908082528060200260200182016040528015610b2c578160200160208202803883390190505b509050606084604051908082528060200260200182016040528015610b5b578160200160208202803883390190505b50905060005b85811015610c38578d8981518110610b7557fe5b6020026020010151858281518110610b8957fe5b6020026020010181815250508c8981518110610ba157fe5b6020026020010151848281518110610bb557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508b8981518110610be157fe5b6020026020010151838281518110610bf557fe5b6020026020010181815250508a8981518110610c0d57fe5b6020026020010151828281518110610c2157fe5b602090810291909101015260019889019801610b61565b610c46338887878787610d2a565b505060019096019550610a69945050505050565b50610c65338a611a0a565b50505050505050505050565b60098054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104d85780601f106104ad576101008083540402835291602001916104d8565b600d546001600160a01b031681565b60006001600160a01b038216610d095760405162461bcd60e51b815260040161050a90612dc8565b6001600160a01b03821660009081526001602052604090206106bb90611860565b600c54604051630ebc905560e11b8152600091829182916001600160a01b031690631d7920aa90610d5f908b90600401612a60565b60606040518083038186803b158015610d7757600080fd5b505afa158015610d8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610daf919081019061292b565b50600b5460405163095addb960e41b81529193506001600160a01b031691506395addb9090610de2908490600401612a60565b6101406040518083038186803b158015610dfb57600080fd5b505afa158015610e0f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e339190810190612549565b509950600097508796505050505050505b8851811015610f2757888181518110610e5957fe5b6020026020010151868281518110610e6d57fe5b6020026020010151888381518110610e8157fe5b60200260200101518a8481518110610e9557fe5b60200260200101516000604051602001610eb3959493929190612984565b604051602081830303815290604052805190602001208414610ed457600080fd5b8a6001600160a01b0316888281518110610eea57fe5b60200260200101516001600160a01b03161415610f0657600191505b888181518110610f1257fe5b60209081029190910101519350600101610e44565b600182151514610f495760405162461bcd60e51b815260040161050a906132be565b5060019a9950505050505050505050565b600c5460405163b31eb89560e01b81526000916060916001600160a01b039091169063b31eb89590610f9290869088906004016133de565b60006040518083038186803b158015610faa57600080fd5b505afa158015610fbe573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610fe69190810190612793565b9150610ff2905061213c565b600d5460405163232e45d760e01b81526001600160a01b039091169063232e45d790611022908790600401612a60565b604080518083038186803b15801561103957600080fd5b505afa15801561104d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061107191908101906126fc565b6020810151815191925090151560011461109d5760405162461bcd60e51b815260040161050a90612eb6565b60008382815181106110ab57fe5b6020026020010151116110d05760405162461bcd60e51b815260040161050a90613302565b50600195945050505050565b60078054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104d85780601f106104ad576101008083540402835291602001916104d8565b6111456117ee565b6001600160a01b0316826001600160a01b031614156111765760405162461bcd60e51b815260040161050a90612c2c565b80600560006111836117ee565b6001600160a01b03908116825260208083019390935260409182016000908120918716808252919093529120805460ff1916921515929092179091556111c76117ee565b6001600160a01b03167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31836040516111ff9190612a55565b60405180910390a35050565b600c546001600160a01b0316156112345760405162461bcd60e51b815260040161050a90612eec565b600d546001600160a01b03161561125d5760405162461bcd60e51b815260040161050a90612eec565b600b546001600160a01b0316156112865760405162461bcd60e51b815260040161050a90612d26565b6001600160a01b0383166112ac5760405162461bcd60e51b815260040161050a90612cef565b6001600160a01b0382166112d25760405162461bcd60e51b815260040161050a9061318e565b6001600160a01b0381166112f85760405162461bcd60e51b815260040161050a906130a1565b600c80546001600160a01b039485166001600160a01b031991821617909155600d805493851693821693909317909255600b8054919093169116179055565b6113486113426117ee565b8361186b565b6113645760405162461bcd60e51b815260040161050a90613201565b61137084848484611ab3565b50505050565b6060611381826117db565b61139d5760405162461bcd60e51b815260040161050a9061301e565b60008281526008602090815260409182902080548351601f60026000196101006001861615020190931692909204918201849004840281018401909452808452606093928301828280156114325780601f1061140757610100808354040283529160200191611432565b820191906000526020600020905b81548152906001019060200180831161141557829003601f168201915b505050505090506060611443610c71565b905080516000141561145757509050610447565b8151156114895780826040516020016114719291906129d6565b60405160208183030381529060405292505050610447565b8061149385611ae6565b6040516020016114a49291906129d6565b60405160208183030381529060405292505050919050565b60008083116114dd5760405162461bcd60e51b815260040161050a9061339a565b600e546000818152600f6020526040902080548690829060ff1916600183600481111561150657fe5b021790555060018101859055604051339083907ff12dd2cec284c7695fa5ff87a683b64add6d47d7a74e190171798ee5e70a744c90611546908890612a81565b60405180910390a35060018101600e55949350505050565b600b546001600160a01b031681565b600e5481565b60006106cd600a611baa565b600c546001600160a01b031681565b600c5460405163b31eb89560e01b815260009182916001600160a01b039091169063b31eb895906115c590869088906004016133de565b60006040518083038186803b1580156115dd57600080fd5b505afa1580156115f1573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526116199190810190612793565b509050806116395760405162461bcd60e51b815260040161050a9061310a565b5060019392505050565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b60006001600160a01b0383166116995760405162461bcd60e51b815260040161050a906131d4565b600c54604051630ebc905560e11b815260009182916001600160a01b0390911690631d7920aa906116ce908790600401612a60565b60606040518083038186803b1580156116e657600080fd5b505afa1580156116fa573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061171e919081019061292b565b50600b5460405163095addb960e41b81529194506001600160a01b031691506395addb9090611751908590600401612a60565b6101406040518083038186803b15801561176a57600080fd5b505afa15801561177e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506117a29190810190612549565b509698505050506001600160a01b038a81169087161494506106b493505050505760405162461bcd60e51b815260040161050a9061306d565b60006106bb60028363ffffffff611bae16565b3390565b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061182782610984565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60006106bb82611baa565b6000611876826117db565b6118925760405162461bcd60e51b815260040161050a90612ca3565b600061189d83610984565b9050806001600160a01b0316846001600160a01b031614806118d85750836001600160a01b03166118cd846104e3565b6001600160a01b0316145b806118e857506118e88185611643565b949350505050565b826001600160a01b031661190382610984565b6001600160a01b0316146119295760405162461bcd60e51b815260040161050a90612fd5565b6001600160a01b03821661194f5760405162461bcd60e51b815260040161050a90612be8565b61195a8383836105c2565b6119656000826117f2565b6001600160a01b038316600090815260016020526040902061198d908263ffffffff611bba16565b506001600160a01b03821660009081526001602052604090206119b6908263ffffffff611bc616565b506119c96002828463ffffffff611bd216565b5080826001600160a01b0316846001600160a01b031660008051602061350d83398151915260405160405180910390a4505050565b60006107518383611be8565b6000611a16600a611c2d565b6000611a22600a611baa565b9050611a2e8482611c36565b6000818152601060209081526040808320869055858352600f82528083206001600160a01b03881684526002019091529020805460ff19166001179055905092915050565b6000808080611a828686611cf4565b9097909650945050505050565b8051611aa2906009906020840190612153565b5050565b60006118e8848484611d50565b611abe8484846118f0565b611aca84848484611daf565b6113705760405162461bcd60e51b815260040161050a90612ad6565b606081611b0b57506040805180820190915260018152600360fc1b6020820152610447565b8160005b8115611b2357600101600a82049150611b0f565b6060816040519080825280601f01601f191660200182016040528015611b50576020820181803883390190505b50859350905060001982015b8315611ba157600a840660300160f81b82828060019003935081518110611b7f57fe5b60200101906001600160f81b031916908160001a905350600a84049350611b5c565b50949350505050565b5490565b60006107518383611e94565b60006107518383611eac565b60006107518383611f72565b60006118e884846001600160a01b038516611fbc565b81546000908210611c0b5760405162461bcd60e51b815260040161050a90612a94565b826000018281548110611c1a57fe5b9060005260206000200154905092915050565b80546001019055565b6001600160a01b038216611c5c5760405162461bcd60e51b815260040161050a90612e81565b611c65816117db565b15611c825760405162461bcd60e51b815260040161050a90612b71565b611c8e600083836105c2565b6001600160a01b0382166000908152600160205260409020611cb6908263ffffffff611bc616565b50611cc96002828463ffffffff611bd216565b5060405181906001600160a01b0384169060009060008051602061350d833981519152908290a45050565b815460009081908310611d195760405162461bcd60e51b815260040161050a90612e3f565b6000846000018481548110611d2a57fe5b906000526020600020906002020190508060000154816001015492509250509250929050565b60008281526001840160205260408120548281611d805760405162461bcd60e51b815260040161050a9190612a81565b50846000016001820381548110611d9357fe5b9060005260206000209060020201600101549150509392505050565b6000611dc3846001600160a01b0316612053565b611dcf575060016118e8565b6060611e5d630a85bd0160e11b611de46117ee565b888787604051602401611dfa9493929190612a18565b604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518060600160405280603281526020016134b2603291396001600160a01b038816919063ffffffff61205916565b9050600081806020019051611e75919081019061260b565b6001600160e01b031916630a85bd0160e11b1492505050949350505050565b60009081526001919091016020526040902054151590565b60008181526001830160205260408120548015611f685783546000198083019190810190600090879083908110611edf57fe5b9060005260206000200154905080876000018481548110611efc57fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080611f2c57fe5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506106bb565b60009150506106bb565b6000611f7e8383611e94565b611fb4575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556106bb565b5060006106bb565b600082815260018401602052604081205480612021575050604080518082018252838152602080820184815286546001818101895560008981528481209551600290930290950191825591519082015586548684528188019092529290912055610751565b8285600001600183038154811061203457fe5b9060005260206000209060020201600101819055506000915050610751565b3b151590565b60606118e884846000858561206d85612053565b6120895760405162461bcd60e51b815260040161050a90613287565b60006060866001600160a01b031685876040516120a691906129ba565b60006040518083038185875af1925050503d80600081146120e3576040519150601f19603f3d011682016040523d82523d6000602084013e6120e8565b606091505b50915091506120f8828286612103565b979650505050505050565b60608315612112575081610751565b8251156121225782518084602001fd5b8160405162461bcd60e51b815260040161050a9190612a81565b604080518082019091526000808252602082015290565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061219457805160ff19168380011785556121c1565b828001600101855582156121c1579182015b828111156121c15782518255916020019190600101906121a6565b506121cd9291506121d1565b5090565b6104e091905b808211156121cd57600081556001016121d7565b80356106bb81613466565b600082601f830112612206578081fd5b81356122196122148261341b565b6133f5565b81815291506020808301908481018184028601820187101561223a57600080fd5b60005b8481101561226257813561225081613466565b8452928201929082019060010161223d565b505050505092915050565b600082601f83011261227d578081fd5b813561228b6122148261341b565b8181529150602080830190848101818402860182018710156122ac57600080fd5b60005b84811015612262578135845292820192908201906001016122af565b600082601f8301126122db578081fd5b81356001600160401b038111156122f0578182fd5b612303601f8201601f19166020016133f5565b915080825283602082850101111561231a57600080fd5b8060208401602084013760009082016020015292915050565b600060208284031215612344578081fd5b813561075181613466565b60008060408385031215612361578081fd5b823561236c81613466565b9150602083013561237c81613466565b809150509250929050565b60008060006060848603121561239b578081fd5b83356123a681613466565b925060208401356123b681613466565b929592945050506040919091013590565b600080600080608085870312156123dc578182fd5b84356123e781613466565b935060208501356123f781613466565b92506040850135915060608501356001600160401b03811115612418578182fd5b612424878288016122cb565b91505092959194509250565b60008060408385031215612442578182fd5b823561244d81613466565b9150602083013561237c8161347b565b6000806040838503121561246f578182fd5b823561247a81613466565b946020939093013593505050565b60008060008060008060c087890312156124a0578384fd5b6124aa88886121eb565b95506020870135945060408701356001600160401b03808211156124cc578586fd5b6124d88a838b0161226d565b955060608901359150808211156124ed578384fd5b6124f98a838b016121f6565b9450608089013591508082111561250e578384fd5b61251a8a838b0161226d565b935060a089013591508082111561252f578283fd5b5061253c89828a0161226d565b9150509295509295509295565b6000806000806000806000806000806101408b8d031215612568578788fd5b8a51995060208b015161257a81613466565b60408c015190995061258b8161349f565b60608c015190985061259c8161349f565b60808c01519097506125ad8161349f565b60a08c01519096506125be8161347b565b8095505060c08b0151935060e08b015192506101008b015191506101208b015190509295989b9194979a5092959850565b600060208284031215612600578081fd5b813561075181613489565b60006020828403121561261c578081fd5b815161075181613489565b60008060006060848603121561263b578081fd5b833561264681613466565b9250602084013561265681613466565b9150604084013561266681613466565b809150509250925092565b600080600060608486031215612685578081fd5b833560058110612693578182fd5b92506020840135915060408401356001600160401b038111156126b4578182fd5b6126c0868287016122cb565b9150509250925092565b6000602082840312156126db578081fd5b81356001600160401b038111156126f0578182fd5b6118e8848285016122cb565b60006040828403121561270d578081fd5b61271760406133f5565b82516127228161347b565b81526020928301519281019290925250919050565b600060208284031215612748578081fd5b5035919050565b60008060408385031215612761578182fd5b8235915060208301356001600160401b0381111561277d578182fd5b6127898582860161226d565b9150509250929050565b600080604083850312156127a5578182fd5b8251602080850151919350906001600160401b038111156127c4578283fd5b80850186601f8201126127d5578384fd5b805191506127e56122148361341b565b82815283810190828501858502840186018a1015612801578687fd5b8693505b84841015612823578051835260019390930192918501918501612805565b5080955050505050509250929050565b600080600080600080600060e0888a03121561284d578081fd5b8735965060208801356001600160401b038082111561286a578283fd5b6128768b838c0161226d565b975060408a013591508082111561288b578283fd5b6128978b838c0161226d565b965060608a01359150808211156128ac578283fd5b6128b88b838c0161226d565b955060808a01359150808211156128cd578283fd5b6128d98b838c016121f6565b945060a08a01359150808211156128ee578283fd5b6128fa8b838c0161226d565b935060c08a013591508082111561290f578283fd5b5061291c8a828b0161226d565b91505092959891949750929550565b60008060006060848603121561293f578081fd5b8351925060208401519150604084015190509250925092565b6000815180845261297081602086016020860161343a565b601f01601f19169290920160200192915050565b94855260208501939093526040840191909152606090811b6001600160601b03191690830152151560f81b607482015260750190565b600082516129cc81846020870161343a565b9190910192915050565b600083516129e881846020880161343a565b83519083016129fb82826020880161343a565b01949350505050565b6001600160a01b0391909116815260200190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612a4b90830184612958565b9695505050505050565b901515815260200190565b90815260200190565b6040810160058410612a7757fe5b9281526020015290565b6000602082526107516020830184612958565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60208082526029908201527f4d61726b65747320636f756e7420616e64206f6363757272656e63657320646f6040820152680dc4ee840dac2e8c6d60bb1b606082015260800190565b6020808252601c908201527b115490cdcc8c4e881d1bdad95b88185b1c9958591e481b5a5b9d195960221b604082015260600190565b60208082526021908201527f7573657220646f6573206e6f7420686f6c64206f7574636f6d652073686172656040820152607360f81b606082015260800190565b60208082526024908201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646040820152637265737360e01b606082015260800190565b60208082526019908201527822a9219b99189d1030b8383937bb32903a379031b0b63632b960391b604082015260600190565b60208082526024908201527f626173655552492063616e206f6e6c7920626520696e697469616c697a6564206040820152636f6e636560e01b606082015260800190565b6020808252602c908201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860408201526b34b9ba32b73a103a37b5b2b760a11b606082015260800190565b6020808252601e908201527f5f70726564696374696f6e4d61726b6574206164647265737320697320300000604082015260600190565b6020808252602a908201527f7265616c6974696f45524332302063616e206f6e6c7920626520696e697469616040820152696c697a6564206f6e636560b01b606082015260800190565b60208082526038908201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f776040820152771b995c881b9bdc88185c1c1c9bdd995908199bdc88185b1b60421b606082015260800190565b6020808252602a908201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604082015269726f206164647265737360b01b606082015260800190565b602080825260139082015272139bc81858dd1a5bdb9cc81c1c9bdd9a591959606a1b604082015260600190565b60208082526022908201527f456e756d657261626c654d61703a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252818101527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604082015260600190565b6020808252601c908201527b1b585c9ad95d081a185cc81b9bdd081899595b881c995cdbdb1d995960221b604082015260600190565b6020808252602d908201527f70726564696374696f6e4d61726b65742063616e206f6e6c7920626520696e6960408201526c7469616c697a6564206f6e636560981b606082015260800190565b60208082526030908201527f4d6574686f64206f6e6c79207573656420666f7220626f6e6420706c6163656d60408201526f656e7420616368696576656d656e747360801b606082015260800190565b6020808252602c908201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860408201526b34b9ba32b73a103a37b5b2b760a11b606082015260800190565b60208082526029908201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960408201526839903737ba1037bbb760b91b606082015260800190565b6020808252602f908201527f4552433732314d657461646174613a2055524920717565727920666f72206e6f60408201526e3732bc34b9ba32b73a103a37b5b2b760891b606082015260800190565b6020808252601a90820152791d5cd95c88191a59081b9bdd0818dc99585d19481b585c9ad95d60321b604082015260600190565b6020808252601b908201527a05f7265616c6974696f45524332302061646472657373206973203602c1b604082015260600190565b6020808252601a908201527924b73b30b634b21030b1b434b2bb32b6b2b73a1030b1ba34b7b760311b604082015260600190565b60208082526023908201527f7573657220646f6573206e6f7420686f6c64206c69717569646974792073686160408201526272657360e81b606082015260800190565b60208082526021908201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656040820152603960f91b606082015260800190565b60208082526026908201527f5f70726564696374696f6e4d61726b65745265736f6c7665722061646472657360408201526507320697320360d41b606082015260800190565b60208082526013908201527207573657220616464726573732069732030783606c1b604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b6020808252601b908201527a1058da1a595d995b595b9d08185b1c9958591e4818db185a5b5959602a1b604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b60208082526024908201527f7573657220686173206e6f7420706c61636564206120626f6e6420696e206d616040820152631c9ad95d60e21b606082015260800190565b60208082526029908201527f7573657220646f6573206e6f7420686f6c642077696e6e696e67206f7574636f6040820152686d652073686172657360b81b606082015260800190565b6020808252602f908201527f4d6574686f64206e6f74207573656420666f7220626f6e6420706c6163656d6560408201526e6e7420616368696576656d656e747360881b606082015260800190565b60208082526024908201527f6f6363757272656e6365732068617320746f20626520677265617465722074686040820152630616e20360e41b606082015260800190565b9182526001600160a01b0316602082015260400190565b6040518181016001600160401b038111828210171561341357600080fd5b604052919050565b60006001600160401b03821115613430578081fd5b5060209081020190565b60005b8381101561345557818101518382015260200161343d565b838111156113705750506000910152565b6001600160a01b038116811461098157600080fd5b801515811461098157600080fd5b6001600160e01b03198116811461098157600080fd5b63ffffffff8116811461098157600080fdfe4552433732313a207472616e7366657220746f206e6f6e20455243373231526563656976657220696d706c656d656e7465724552433732313a206f776e657220717565727920666f72206e6f6e6578697374656e7420746f6b656eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220e731d2d277243cb26ce67dd65f8de00b43380526a9a0457ad6a480b5a9a8b17364736f6c63430006020033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000a49464c205469746c657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000449464c5400000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : token (string): IFL Titles
Arg [1] : ticker (string): IFLT

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [3] : 49464c205469746c657300000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [5] : 49464c5400000000000000000000000000000000000000000000000000000000


Deployed Bytecode Sourcemap

178245:8933:0:-:0;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;178245:8933:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;147371:150;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;163555:100;;;:::i;:::-;;;;;;;;166341:221;;;;;;;;;:::i;:::-;;;;;;;;165871:404;;;;;;;;;:::i;:::-;;181443:513;;;;;;;;;:::i;165349:211::-;;;:::i;:::-;;;;;;;;167231:305;;;;;;;;;:::i;179361:51::-;;;;;;;;;:::i;:::-;;;;;;;;;165111:162;;;;;;;;;:::i;167607:151::-;;;;;;;;;:::i;181224:213::-;;;;;;;;;:::i;184206:1151::-;;;;;;;;;:::i;179417:41::-;;;;;;;;;:::i;165637:172::-;;;;;;;;;:::i;180503:171::-;;;;;;;;;:::i;163311:177::-;;;;;;;;;:::i;185363:1345::-;;;;;;;;;:::i;164930:97::-;;;:::i;178504:56::-;;;:::i;163028:221::-;;;;;;;;;:::i;182271:864::-;;;;;;;;;:::i;183141:619::-;;;;;;;;;:::i;163724:104::-;;;:::i;166634:295::-;;;;;;;;;:::i;179585:912::-;;;;;;;;;:::i;167829:285::-;;;;;;;;;:::i;163899:792::-;;;;;;;;;:::i;180680:538::-;;;;;;;;;:::i;178418:34::-;;;:::i;179321:35::-;;;:::i;187084:91::-;;;:::i;178457:42::-;;;:::i;181962:303::-;;;;;;;;;:::i;167000:164::-;;;;;;;;;:::i;183766:434::-;;;;;;;;;:::i;147371:150::-;-1:-1:-1;;;;;;147480:33:0;;147456:4;147480:33;;;;;;;;;;;;;147371:150;;;;:::o;163555:100::-;163642:5;163635:12;;;;;;;;-1:-1:-1;;163635:12:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;163609:13;;163635:12;;163642:5;;163635:12;;163642:5;163635:12;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;163555:100;;:::o;166341:221::-;166417:7;166445:16;166453:7;166445;:16::i;:::-;166437:73;;;;-1:-1:-1;;;166437:73:0;;;;;;;;;;;;;;;;;-1:-1:-1;166530:24:0;;;;:15;:24;;;;;;-1:-1:-1;;;;;166530:24:0;;166341:221::o;165871:404::-;165952:13;165968:23;165983:7;165968:14;:23::i;:::-;165952:39;;166016:5;-1:-1:-1;;;;;166010:11:0;:2;-1:-1:-1;;;;;166010:11:0;;;166002:57;;;;-1:-1:-1;;;166002:57:0;;;;;;;;;166096:5;-1:-1:-1;;;;;166080:21:0;:12;:10;:12::i;:::-;-1:-1:-1;;;;;166080:21:0;;:69;;;;166105:44;166129:5;166136:12;:10;:12::i;166105:44::-;166072:161;;;;-1:-1:-1;;;166072:161:0;;;;;;;;;166246:21;166255:2;166259:7;166246:8;:21::i;:::-;165871:404;;;:::o;181443:513::-;181599:16;;:52;;-1:-1:-1;;;181599:52:0;;181529:4;;181542:30;;-1:-1:-1;;;;;181599:16:0;;;;:36;;:52;;181636:8;;181646:4;;181599:52;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;181599:52:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;181599:52:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;181599:52:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;181599:52:0;;;;;;;;;181579:72;-1:-1:-1;181660:24:0;;-1:-1:-1;181660:24:0;181693:156;181717:13;:20;181713:1;:24;181693:156;;;181776:1;181757:13;181771:1;181757:16;;;;;;;;;;;;;;:20;181753:89;;;181812:4;181790:26;;181827:5;;181753:89;181739:3;;181693:156;;;-1:-1:-1;181888:4:0;181865:27;;;;181857:73;;;;-1:-1:-1;;;181857:73:0;;;;;;;;;181946:4;181939:11;;;;181443:513;;;;;:::o;165349:211::-;165410:7;165531:21;:12;:19;:21::i;:::-;165524:28;;165349:211;:::o;167231:305::-;167392:41;167411:12;:10;:12::i;:::-;167425:7;167392:18;:41::i;:::-;167384:103;;;;-1:-1:-1;;;167384:103:0;;;;;;;;;167500:28;167510:4;167516:2;167520:7;167500:9;:28::i;179361:51::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;165111:162::-;-1:-1:-1;;;;;165235:20:0;;165208:7;165235:20;;;:13;:20;;;;;:30;;165259:5;165235:30;:23;:30;:::i;:::-;165228:37;165111:162;-1:-1:-1;;;165111:162:0:o;167607:151::-;167711:39;167728:4;167734:2;167738:7;167711:39;;;;;;;;;;;;:16;:39::i;181224:213::-;181317:4;181364:27;;;:12;:27;;;;;;;;-1:-1:-1;;;;;181407:24:0;;;;;;:18;;;;:24;;;;;;;181224:213::o;184206:1151::-;184299:7;184349:27;;;:12;:27;;;;;;;;184412:10;184393:30;;:18;;;:30;;;;;;;;:39;184385:79;;;;-1:-1:-1;;;184385:79:0;;;;;;;;;184501:11;184479:18;;;;:33;;;;;;;;;;184471:93;;;;-1:-1:-1;;;184471:93:0;;;;;;;;;184599:11;:23;;;184579:9;:16;:43;184571:97;;;;-1:-1:-1;;;184571:97:0;;;;;;;;;184682:9;184677:624;184701:9;:16;184697:1;:20;184677:624;;;184733:16;184752:9;184762:1;184752:12;;;;;;;;;;;;;;184733:31;;184801:10;184779:32;;;;;;;;:18;;;;:32;;;;;;;;;184775:519;;;184824:45;184848:10;184860:8;184824:23;:45::i;:::-;;184775:519;;;184911:19;184889:18;;;;:41;;;;;;;;;184885:409;;;184943:43;184965:10;184977:8;184943:21;:43::i;184885:409::-;185028:20;185006:18;;;;:42;;;;;;;;;185002:292;;;185061:44;185084:10;185096:8;185061:22;:44::i;185002:292::-;185147:19;185125:18;;;;:41;;;;;;;;;185121:173;;;185179:42;185200:10;185212:8;185179:20;:42::i;185121:173::-;185248:36;;-1:-1:-1;;;185248:36:0;;;;;;;;185121:173;-1:-1:-1;184719:3:0;;184677:624;;;;185309:42;185325:10;185337:13;185309:15;:42::i;:::-;;184206:1151;;;;;:::o;179417:41::-;;;;;;;;;;;;;:::o;165637:172::-;165712:7;;165754:22;:12;165770:5;165754:22;:15;:22;:::i;:::-;-1:-1:-1;165732:44:0;165637:172;-1:-1:-1;;;165637:172:0:o;180503:171::-;180575:9;:7;:9::i;:::-;180569:23;:28;180561:77;;;;-1:-1:-1;;;180561:77:0;;;;;;;;;180647:21;180659:8;180647:11;:21::i;:::-;180503:171;:::o;163311:177::-;163383:7;163410:70;163427:7;163410:70;;;;;;;;;;;;;;;;;:12;;:70;;:16;:70;:::i;185363:1345::-;185627:31;185661:27;;;:12;:27;;;;;;;;185724:10;185705:30;;:18;;;:30;;;;;;;;;:39;185697:79;;;;-1:-1:-1;;;185697:79:0;;;;;;;;;185813:11;185791:18;;;;:33;;;;;;;;;185783:94;;;;-1:-1:-1;;;185783:94:0;;;;;;;;;185911:1;185892:9;:16;:20;185884:52;;;;-1:-1:-1;;;185884:52:0;;;;;;;;;185971:11;:23;;;185951:9;:16;:43;185943:97;;;;-1:-1:-1;;;185943:97:0;;;;;;;;;186049:10;;186068:584;186092:9;:16;186088:1;:20;186068:584;;;186124:16;186143:9;186153:1;186143:12;;;;;;;;;;;;;;186124:31;;186166:10;186179:7;186187:1;186179:10;;;;;;;;;;;;;;186166:23;;186198:19;186234:2;186220:17;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;186220:17:0;;186198:39;;186246:19;186282:2;186268:17;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;186268:17:0;;186246:39;;186294:19;186330:2;186316:17;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;186316:17:0;;186294:39;;186342:19;186378:2;186364:17;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;186364:17:0;-1:-1:-1;186342:39:0;-1:-1:-1;186390:9:0;186408:173;186424:2;186420:1;:6;186408:173;;;186452:14;186467:2;186452:18;;;;;;;;;;;;;;186444:2;186447:1;186444:5;;;;;;;;;;;;;:26;;;;;186489:5;186495:2;186489:9;;;;;;;;;;;;;;186481:2;186484:1;186481:5;;;;;;;;;;;;;:17;-1:-1:-1;;;;;186481:17:0;;;-1:-1:-1;;;;;186481:17:0;;;;;186517:5;186523:2;186517:9;;;;;;;;;;;;;;186509:2;186512:1;186509:5;;;;;;;;;;;;;:17;;;;;186545:7;186553:2;186545:11;;;;;;;;;;;;;;186537:2;186540:1;186537:5;;;;;;;;;;;;;;;;;:19;186567:4;;;;;186428:3;186408:173;;;186589:55;186607:10;186619:8;186629:2;186633;186637;186641;186589:17;:55::i;:::-;-1:-1:-1;;186110:3:0;;;;;-1:-1:-1;186068:584:0;;-1:-1:-1;;;;;186068:584:0;;;186660:42;186676:10;186688:13;186660:15;:42::i;:::-;;185363:1345;;;;;;;;;:::o;164930:97::-;165011:8;165004:15;;;;;;;;-1:-1:-1;;165004:15:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;164978:13;;165004:15;;165011:8;;165004:15;;165011:8;165004:15;;;;;;;;;;;;;;;;;;;;;;;;178504:56;;;-1:-1:-1;;;;;178504:56:0;;:::o;163028:221::-;163100:7;-1:-1:-1;;;;;163128:19:0;;163120:74;;;;-1:-1:-1;;;163120:74:0;;;;;;;;;-1:-1:-1;;;;;163212:20:0;;;;;;:13;:20;;;;;:29;;:27;:29::i;182271:864::-;182582:16;;:43;;-1:-1:-1;;;182582:43:0;;182493:4;;;;;;-1:-1:-1;;;;;182582:16:0;;:33;;:43;;182616:8;;182582:43;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;182582:43:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;182582:43: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;182582:43:0;;;;;;;;;-1:-1:-1;182672:13:0;;:35;;-1:-1:-1;;;182672:35:0;;182563:62;;-1:-1:-1;;;;;;182672:13:0;;-1:-1:-1;182672:23:0;;:35;;182563:62;;182672:35;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;182672:35:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;182672:35: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;182672:35:0;;;;;;;;;-1:-1:-1;182632:75:0;-1:-1:-1;182714:11:0;;-1:-1:-1;182714:11:0;;-1:-1:-1;;;;;;;182750:288:0;182766:14;:21;182762:1;:25;182750:288;;;182869:14;182884:1;182869:17;;;;;;;;;;;;;;182888:7;182896:1;182888:10;;;;;;;;;;;;;;182900:5;182906:1;182900:8;;;;;;;;;;;;;;182910:5;182916:1;182910:8;;;;;;;;;;;;;;182920:5;182852:74;;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;182852:74:0;;;182842:85;;;;;;182821:17;:106;182803:133;;;;;;182963:4;-1:-1:-1;;;;;182951:16:0;:5;182957:1;182951:8;;;;;;;;;;;;;;-1:-1:-1;;;;;182951:16:0;;182947:35;;;182978:4;182969:13;;182947:35;183013:14;183028:1;183013:17;;;;;;;;;;;;;;;;;;;-1:-1:-1;182789:3:0;;182750:288;;;183064:4;183054:14;;;;183046:63;;;;-1:-1:-1;;;183046:63:0;;;;;;;;;-1:-1:-1;183125:4:0;;182271:864;-1:-1:-1;;;;;;;;;;182271:864:0:o;183141:619::-;183296:16;;:52;;-1:-1:-1;;;183296:52:0;;183226:4;;183239:30;;-1:-1:-1;;;;;183296:16:0;;;;:36;;:52;;183333:8;;183343:4;;183296:52;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;183296:52:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;183296:52:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;183296:52:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;183296:52:0;;;;;;;;;183276:72;-1:-1:-1;183355:65:0;;-1:-1:-1;183355:65:0;:::i;:::-;183423:24;;:68;;-1:-1:-1;;;183423:68:0;;-1:-1:-1;;;;;183423:24:0;;;;:44;;:68;;183476:8;;183423:68;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;183423:68:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;183423:68: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;183423:68:0;;;;;;;;;183528:26;;;;183571:25;;183528:26;;-1:-1:-1;183528:26:0;183571:33;;183600:4;183571:33;183563:74;;;;-1:-1:-1;;;183563:74:0;;;;;;;;;183687:1;183652:13;183666:17;183652:32;;;;;;;;;;;;;;:36;183644:90;;;;-1:-1:-1;;;183644:90:0;;;;;;;;;-1:-1:-1;183750:4:0;;183141:619;-1:-1:-1;;;;;183141:619:0:o;163724:104::-;163813:7;163806:14;;;;;;;;-1:-1:-1;;163806:14:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;163780:13;;163806:14;;163813:7;;163806:14;;163813:7;163806:14;;;;;;;;;;;;;;;;;;;;;;;;166634:295;166749:12;:10;:12::i;:::-;-1:-1:-1;;;;;166737:24:0;:8;-1:-1:-1;;;;;166737:24:0;;;166729:62;;;;-1:-1:-1;;;166729:62:0;;;;;;;;;166849:8;166804:18;:32;166823:12;:10;:12::i;:::-;-1:-1:-1;;;;;166804:32:0;;;;;;;;;;;;;;;;;-1:-1:-1;166804:32:0;;;:42;;;;;;;;;;;;:53;;-1:-1:-1;;166804:53:0;;;;;;;;;;;166888:12;:10;:12::i;:::-;-1:-1:-1;;;;;166873:48:0;;166912:8;166873:48;;;;;;;;;;;;;;;166634:295;;:::o;179585:912::-;179777:16;;-1:-1:-1;;;;;179777:16:0;179769:39;179761:97;;;;-1:-1:-1;;;179761:97:0;;;;;;;;;179881:24;;-1:-1:-1;;;;;179881:24:0;179873:47;179865:105;;;;-1:-1:-1;;;179865:105:0;;;;;;;;;179993:13;;-1:-1:-1;;;;;179993:13:0;179985:36;179977:91;;;;-1:-1:-1;;;179977:91:0;;;;;;;;;-1:-1:-1;;;;;180085:40:0;;180077:83;;;;-1:-1:-1;;;180077:83:0;;;;;;;;;-1:-1:-1;;;;;180175:48:0;;180167:99;;;;-1:-1:-1;;;180167:99:0;;;;;;;;;-1:-1:-1;;;;;180281:37:0;;180273:77;;;;-1:-1:-1;;;180273:77:0;;;;;;;;;180359:16;:36;;-1:-1:-1;;;;;180359:36:0;;;-1:-1:-1;;;;;;180359:36:0;;;;;;;180402:24;:52;;;;;;;;;;;;;;;180461:13;:30;;;;;;;;;;;179585:912::o;167829:285::-;167961:41;167980:12;:10;:12::i;:::-;167994:7;167961:18;:41::i;:::-;167953:103;;;;-1:-1:-1;;;167953:103:0;;;;;;;;;168067:39;168081:4;168087:2;168091:7;168100:5;168067:13;:39::i;:::-;167829:285;;;;:::o;163899:792::-;163972:13;164006:16;164014:7;164006;:16::i;:::-;163998:76;;;;-1:-1:-1;;;163998:76:0;;;;;;;;;164113:19;;;;:10;:19;;;;;;;;;164087:45;;;;;;-1:-1:-1;;164087:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:23;;:45;;;164113:19;164087:45;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;164143:18;164164:9;:7;:9::i;:::-;164143:30;;164255:4;164249:18;164271:1;164249:23;164245:72;;;-1:-1:-1;164296:9:0;-1:-1:-1;164289:16:0;;164245:72;164421:23;;:27;164417:108;;164496:4;164502:9;164479:33;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;164479:33:0;;;164465:48;;;;;;164417:108;164657:4;164663:18;:7;:16;:18::i;:::-;164640:42;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;164640:42:0;;;164626:57;;;;163899:792;;;:::o;180680:538::-;180802:7;180840:1;180826:11;:15;180818:64;;;;-1:-1:-1;;;180818:64:0;;;;;;;;;180913:16;;180889:21;180970:27;;;:12;:27;;;;;181006;;181027:6;;180970:27;;-1:-1:-1;;181006:27:0;;181027:6;181006:27;;;;;;;;;;;;-1:-1:-1;181040:23:0;;;:37;;;181089:53;;181122:10;;181107:13;;181089:53;;;;181134:7;;181089:53;;;;;;;;;;-1:-1:-1;181184:1:0;181168:17;;181149:16;:36;181168:17;180680:538;-1:-1:-1;;;;180680:538:0:o;178418:34::-;;;-1:-1:-1;;;;;178418:34:0;;:::o;179321:35::-;;;;:::o;187084:91::-;187127:7;187150:19;:9;:17;:19::i;178457:42::-;;;-1:-1:-1;;;;;178457:42:0;;:::o;181962:303::-;182111:16;;:52;;-1:-1:-1;;;182111:52:0;;182046:4;;;;-1:-1:-1;;;;;182111:16:0;;;;:36;;:52;;182148:8;;182158:4;;182111:52;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;182111:52:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;182111:52:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;182111:52:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;182111:52:0;;;;;;;;;-1:-1:-1;182089:74:0;-1:-1:-1;182180:19:0;182172:67;;;;-1:-1:-1;;;182172:67:0;;;;;;;;;-1:-1:-1;182255:4:0;;181962:303;-1:-1:-1;;;181962:303:0:o;167000:164::-;-1:-1:-1;;;;;167121:25:0;;;167097:4;167121:25;;;:18;:25;;;;;;;;:35;;;;;;;;;;;;;;;167000:164::o;183766:434::-;183849:4;-1:-1:-1;;;;;183870:18:0;;183862:50;;;;-1:-1:-1;;;183862:50:0;;;;;;;;;183990:16;;:43;;-1:-1:-1;;;183990:43:0;;183921:18;;;;-1:-1:-1;;;;;183990:16:0;;;;:33;;:43;;184024:8;;183990:43;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;183990:43:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;183990:43: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;183990:43:0;;;;;;;;;-1:-1:-1;184073:13:0;;:35;;-1:-1:-1;;;184073:35:0;;183971:62;;-1:-1:-1;;;;;;184073:13:0;;-1:-1:-1;184073:23:0;;:35;;183971:62;;184073:35;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;184073:35:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;184073:35: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;184073:35:0;;;;;;;;;-1:-1:-1;184040:68:0;;-1:-1:-1;;;;;;;;;184125:18:0;;;;;;;;-1:-1:-1;184117:57:0;;-1:-1:-1;;;;184117:57:0;;;-1:-1:-1;;;184117:57:0;;;;;;;;169581:127;169646:4;169670:30;:12;169692:7;169670:30;:21;:30;:::i;101687:106::-;101775:10;101687:106;:::o;175599:192::-;175674:24;;;;:15;:24;;;;;:29;;-1:-1:-1;;;;;;175674:29:0;-1:-1:-1;;;;;175674:29:0;;;;;;;;:24;;175728:23;175674:24;175728:14;:23::i;:::-;-1:-1:-1;;;;;175719:46:0;;;;;;;;;;;175599:192;;:::o;156232:123::-;156301:7;156328:19;156336:3;156328:7;:19::i;169875:355::-;169968:4;169993:16;170001:7;169993;:16::i;:::-;169985:73;;;;-1:-1:-1;;;169985:73:0;;;;;;;;;170069:13;170085:23;170100:7;170085:14;:23::i;:::-;170069:39;;170138:5;-1:-1:-1;;;;;170127:16:0;:7;-1:-1:-1;;;;;170127:16:0;;:51;;;;170171:7;-1:-1:-1;;;;;170147:31:0;:20;170159:7;170147:11;:20::i;:::-;-1:-1:-1;;;;;170147:31:0;;170127:51;:94;;;;170182:39;170206:5;170213:7;170182:23;:39::i;:::-;170119:103;169875:355;-1:-1:-1;;;;169875:355:0:o;173011:599::-;173136:4;-1:-1:-1;;;;;173109:31:0;:23;173124:7;173109:14;:23::i;:::-;-1:-1:-1;;;;;173109:31:0;;173101:85;;;;-1:-1:-1;;;173101:85:0;;;;;;;;;-1:-1:-1;;;;;173223:16:0;;173215:65;;;;-1:-1:-1;;;173215:65:0;;;;;;;;;173293:39;173314:4;173320:2;173324:7;173293:20;:39::i;:::-;173397:29;173414:1;173418:7;173397:8;:29::i;:::-;-1:-1:-1;;;;;173439:19:0;;;;;;:13;:19;;;;;:35;;173466:7;173439:35;:26;:35;:::i;:::-;-1:-1:-1;;;;;;173485:17:0;;;;;;:13;:17;;;;;:30;;173507:7;173485:30;:21;:30;:::i;:::-;-1:-1:-1;173528:29:0;:12;173545:7;173554:2;173528:29;:16;:29;:::i;:::-;;173594:7;173590:2;-1:-1:-1;;;;;173575:27:0;173584:4;-1:-1:-1;;;;;173575:27:0;-1:-1:-1;;;;;;;;;;;173575:27:0;;;;;;;;;173011:599;;;:::o;92812:137::-;92883:7;92918:22;92922:3;92934:5;92918:3;:22::i;186714:364::-;186793:7;186809:21;:9;:19;:21::i;:::-;186839:15;186857:19;:9;:17;:19::i;:::-;186839:37;;186883:20;186889:4;186895:7;186883:5;:20::i;:::-;186910:15;;;;:6;:15;;;;;;;;:31;;;186984:27;;;:12;:27;;;;;-1:-1:-1;;;;;187018:24:0;;;;:18;;:24;;;;;:31;;-1:-1:-1;;187018:31:0;187045:4;187018:31;;;186917:7;-1:-1:-1;186714:364:0;;;;:::o;156694:236::-;156774:7;;;;156834:22;156838:3;156850:5;156834:3;:22::i;:::-;156803:53;;;;-1:-1:-1;156694:236:0;-1:-1:-1;;;;;156694:236:0:o;174211:100::-;174284:19;;;;:8;;:19;;;;;:::i;:::-;;174211:100;:::o;157980:213::-;158087:7;158138:44;158143:3;158163;158169:12;158138:4;:44::i;168996:272::-;169110:28;169120:4;169126:2;169130:7;169110:9;:28::i;:::-;169157:48;169180:4;169186:2;169190:7;169199:5;169157:22;:48::i;:::-;169149:111;;;;-1:-1:-1;;;169149:111:0;;;;;;;;158475:746;158531:13;158752:10;158748:53;;-1:-1:-1;158779:10:0;;;;;;;;;;;;-1:-1:-1;;;158779:10:0;;;;;;158748:53;158826:5;158811:12;158867:78;158874:9;;158867:78;;158900:8;;158931:2;158923:10;;;;158867:78;;;158955:19;158987:6;158977:17;;;;;;;;;;;;;;;;;;;;;;;;;21:6:-1;;104:10;158977:17:0;87:34:-1;135:17;;-1:-1;158977:17:0;-1:-1:-1;159049:5:0;;-1:-1:-1;158955:39:0;-1:-1:-1;;;159021:10:0;;159065:117;159072:9;;159065:117;;159141:2;159134:4;:9;159129:2;:14;159116:29;;159098:6;159105:7;;;;;;;159098:15;;;;;;;;;;;:47;-1:-1:-1;;;;;159098:47:0;;;;;;;;-1:-1:-1;159168:2:0;159160:10;;;;159065:117;;;-1:-1:-1;159206:6:0;158475:746;-1:-1:-1;;;;158475:746:0:o;177660:114::-;177752:14;;177660:114::o;155993:151::-;156077:4;156101:35;156111:3;156131;156101:9;:35::i;91899:137::-;91969:4;91993:35;92001:3;92021:5;91993:7;:35::i;91592:131::-;91659:4;91683:32;91688:3;91708:5;91683:4;:32::i;155416:185::-;155505:4;155529:64;155534:3;155554;-1:-1:-1;;;;;155568:23:0;;155529:4;:64::i;87850:204::-;87945:18;;87917:7;;87945:26;-1:-1:-1;87937:73:0;;;;-1:-1:-1;;;87937:73:0;;;;;;;;;88028:3;:11;;88040:5;88028:18;;;;;;;;;;;;;;;;88021:25;;87850:204;;;;:::o;177782:181::-;177936:19;;177954:1;177936:19;;;177782:181::o;171496:404::-;-1:-1:-1;;;;;171576:16:0;;171568:61;;;;-1:-1:-1;;;171568:61:0;;;;;;;;;171649:16;171657:7;171649;:16::i;:::-;171648:17;171640:58;;;;-1:-1:-1;;;171640:58:0;;;;;;;;;171711:45;171740:1;171744:2;171748:7;171711:20;:45::i;:::-;-1:-1:-1;;;;;171769:17:0;;;;;;:13;:17;;;;;:30;;171791:7;171769:30;:21;:30;:::i;:::-;-1:-1:-1;171812:29:0;:12;171829:7;171838:2;171812:29;:16;:29;:::i;:::-;-1:-1:-1;171859:33:0;;171884:7;;-1:-1:-1;;;;;171859:33:0;;;171876:1;;-1:-1:-1;;;;;;;;;;;171859:33:0;171876:1;;171859:33;171496:404;;:::o;153276:279::-;153380:19;;153343:7;;;;153380:27;-1:-1:-1;153372:74:0;;;;-1:-1:-1;;;153372:74:0;;;;;;;;;153459:22;153484:3;:12;;153497:5;153484:19;;;;;;;;;;;;;;;;;;153459:44;;153522:5;:10;;;153534:5;:12;;;153514:33;;;;;153276:279;;;;;:::o;154773:319::-;154867:7;154906:17;;;:12;;;:17;;;;;;154957:12;154942:13;154934:36;;;;-1:-1:-1;;;154934:36:0;;;;;;;;;;;155024:3;:12;;155048:1;155037:8;:12;155024:26;;;;;;;;;;;;;;;;;;:33;;;155017:40;;;154773:319;;;;;:::o;174876:604::-;174997:4;175024:15;:2;-1:-1:-1;;;;;175024:13:0;;:15::i;:::-;175019:60;;-1:-1:-1;175063:4:0;175056:11;;175019:60;175089:23;175115:252;-1:-1:-1;;;175228:12:0;:10;:12::i;:::-;175255:4;175274:7;175296:5;175131:181;;;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;175131:181:0;;;;-1:-1:-1;;;;;175131:181:0;;38:4:-1;29:7;25:18;67:10;61:17;-1:-1;;;;;199:8;192:4;186;182:15;179:29;167:10;160:49;0:215;;;175131:181:0;175115:252;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;175115:15:0;;;:252;;:15;:252;:::i;:::-;175089:278;;175378:13;175405:10;175394:32;;;;;;;;;;;;;;-1:-1:-1;;;;;;175445:26:0;-1:-1:-1;;;175445:26:0;;-1:-1:-1;;;174876:604:0;;;;;;:::o;152591:125::-;152662:4;152686:17;;;:12;;;;;:17;;;;;;:22;;;152591:125::o;85552:1544::-;85618:4;85757:19;;;:12;;;:19;;;;;;85793:15;;85789:1300;;86228:18;;-1:-1:-1;;86179:14:0;;;;86228:22;;;;86155:21;;86228:3;;:22;;86515;;;;;;;;;;;;;;86495:42;;86661:9;86632:3;:11;;86644:13;86632:26;;;;;;;;;;;;;;;;;;;:38;;;;86738:23;;;86780:1;86738:12;;;:23;;;;;;86764:17;;;86738:43;;86890:17;;86738:3;;86890:17;;;;;;;;;;;;;;;;;;;;;;86985:3;:12;;:19;86998:5;86985:19;;;;;;;;;;;86978:26;;;87028:4;87021:11;;;;;;;;85789:1300;87072:5;87065:12;;;;;84962:414;85025:4;85047:21;85057:3;85062:5;85047:9;:21::i;:::-;85042:327;;-1:-1:-1;27:10;;39:1;23:18;;;45:23;;85085:11:0;:23;;;;;;;;;;;;;85268:18;;85246:19;;;:12;;;:19;;;;;;:40;;;;85301:11;;85042:327;-1:-1:-1;85352:5:0;85345:12;;150091:692;150167:4;150302:17;;;:12;;;:17;;;;;;150336:13;150332:444;;-1:-1:-1;;150421:38:0;;;;;;;;;;;;;;;;;;27:10:-1;;39:1;23:18;;;45:23;;150403:12:0;:57;;;;;;;;;;;;;;;;;;;;;;;;150618:19;;150598:17;;;:12;;;:17;;;;;;;:39;150652:11;;150332:444;150732:5;150696:3;:12;;150720:1;150709:8;:12;150696:26;;;;;;;;;;;;;;;;;;:33;;:41;;;;150759:5;150752:12;;;;;93761:422;94128:20;94167:8;;;93761:422::o;96679:195::-;96782:12;96814:52;96836:6;96844:4;96850:1;96853:12;96782;97983:18;97994:6;97983:10;:18::i;:::-;97975:60;;;;-1:-1:-1;;;97975:60:0;;;;;;;;;98109:12;98123:23;98150:6;-1:-1:-1;;;;;98150:11:0;98170:5;98178:4;98150:33;;;;;;;;;;;;;;;;;;;;;;;;12:1:-1;19;14:27;;;;67:4;61:11;56:16;;134:4;130:9;123:4;105:16;101:27;97:43;94:1;90:51;84:4;77:65;157:16;154:1;147:27;211:16;208:1;201:4;198:1;194:12;179:49;5:228;;14:27;32:4;27:9;;5:228;;98108:75:0;;;;98201:52;98219:7;98228:10;98240:12;98201:17;:52::i;:::-;98194:59;97731:530;-1:-1:-1;;;;;;;97731:530:0:o;100271:742::-;100386:12;100415:7;100411:595;;;-1:-1:-1;100446:10:0;100439:17;;100411:595;100560:17;;:21;100556:439;;100823:10;100817:17;100884:15;100871:10;100867:2;100863:19;100856:44;100771:148;100966:12;100959:20;;-1:-1:-1;;;100959:20:0;;;;;;;;;178245:8933;;;;;;;;;;-1:-1:-1;178245:8933:0;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;178245:8933:0;;;-1:-1:-1;178245:8933:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;5:130:-1;72:20;;97:33;72:20;97:33;;301:707;;418:3;411:4;403:6;399:17;395:27;385:2;;-1:-1;;426:12;385:2;473:6;460:20;495:80;510:64;567:6;510:64;;;495:80;;;603:21;;;486:89;-1:-1;647:4;660:14;;;;635:17;;;749;;;740:27;;;;737:36;-1:-1;734:2;;;786:1;;776:12;734:2;811:1;796:206;821:6;818:1;815:13;796:206;;;85:6;72:20;97:33;124:5;97:33;;;889:50;;953:14;;;;981;;;;843:1;836:9;796:206;;;800:14;;;;;378:630;;;;;1034:707;;1151:3;1144:4;1136:6;1132:17;1128:27;1118:2;;-1:-1;;1159:12;1118:2;1206:6;1193:20;1228:80;1243:64;1300:6;1243:64;;1228:80;1336:21;;;1219:89;-1:-1;1380:4;1393:14;;;;1368:17;;;1482;;;1473:27;;;;1470:36;-1:-1;1467:2;;;1519:1;;1509:12;1467:2;1544:1;1529:206;1554:6;1551:1;1548:13;1529:206;;;3563:20;;1622:50;;1686:14;;;;1714;;;;1576:1;1569:9;1529:206;;4049:440;;4150:3;4143:4;4135:6;4131:17;4127:27;4117:2;;-1:-1;;4158:12;4117:2;4192:20;;-1:-1;;;;;56906:30;;56903:2;;;-1:-1;;56939:12;56903:2;4227:64;57012:9;56993:17;;-1:-1;;56989:33;57080:4;57070:15;4227:64;;;4218:73;;4311:6;4304:5;4297:21;4415:3;57080:4;4406:6;4339;4397:16;;4394:25;4391:2;;;4432:1;;4422:12;4391:2;60873:6;57080:4;4339:6;4335:17;57080:4;4373:5;4369:16;60850:30;60929:1;60911:16;;;57080:4;60911:16;60904:27;4373:5;4110:379;-1:-1;;4110:379;6678:241;;6782:2;6770:9;6761:7;6757:23;6753:32;6750:2;;;-1:-1;;6788:12;6750:2;85:6;72:20;97:33;124:5;97:33;;6926:366;;;7047:2;7035:9;7026:7;7022:23;7018:32;7015:2;;;-1:-1;;7053:12;7015:2;85:6;72:20;97:33;124:5;97:33;;;7105:63;-1:-1;7205:2;7244:22;;72:20;97:33;72:20;97:33;;;7213:63;;;;7009:283;;;;;;7299:491;;;;7437:2;7425:9;7416:7;7412:23;7408:32;7405:2;;;-1:-1;;7443:12;7405:2;85:6;72:20;97:33;124:5;97:33;;;7495:63;-1:-1;7595:2;7634:22;;72:20;97:33;72:20;97:33;;;7399:391;;7603:63;;-1:-1;;;7703:2;7742:22;;;;6328:20;;7399:391;7797:721;;;;;7961:3;7949:9;7940:7;7936:23;7932:33;7929:2;;;-1:-1;;7968:12;7929:2;85:6;72:20;97:33;124:5;97:33;;;8020:63;-1:-1;8120:2;8159:22;;72:20;97:33;72:20;97:33;;;8128:63;-1:-1;8228:2;8267:22;;6328:20;;-1:-1;8364:2;8349:18;;8336:32;-1:-1;;;;;8377:30;;8374:2;;;-1:-1;;8410:12;8374:2;8440:62;8494:7;8485:6;8474:9;8470:22;8440:62;;;8430:72;;;7923:595;;;;;;;;8525:360;;;8643:2;8631:9;8622:7;8618:23;8614:32;8611:2;;;-1:-1;;8649:12;8611:2;85:6;72:20;97:33;124:5;97:33;;;8701:63;-1:-1;8801:2;8837:22;;3294:20;3319:30;3294:20;3319:30;;8892:366;;;9013:2;9001:9;8992:7;8988:23;8984:32;8981:2;;;-1:-1;;9019:12;8981:2;85:6;72:20;97:33;124:5;97:33;;;9071:63;9171:2;9210:22;;;;6328:20;;-1:-1;;;8975:283;9265:1413;;;;;;;9554:3;9542:9;9533:7;9529:23;9525:33;9522:2;;;-1:-1;;9561:12;9522:2;9623:53;9668:7;9644:22;9623:53;;;9613:63;-1:-1;9713:2;9752:22;;6328:20;;-1:-1;9849:2;9834:18;;9821:32;-1:-1;;;;;9862:30;;;9859:2;;;-1:-1;;9895:12;9859:2;9925:78;9995:7;9986:6;9975:9;9971:22;9925:78;;;9915:88;;10068:2;10057:9;10053:18;10040:32;10026:46;;9873:18;10084:6;10081:30;10078:2;;;-1:-1;;10114:12;10078:2;10144:78;10214:7;10205:6;10194:9;10190:22;10144:78;;;10134:88;;10287:3;10276:9;10272:19;10259:33;10245:47;;9873:18;10304:6;10301:30;10298:2;;;-1:-1;;10334:12;10298:2;10364:78;10434:7;10425:6;10414:9;10410:22;10364:78;;;10354:88;;10507:3;10496:9;10492:19;10479:33;10465:47;;9873:18;10524:6;10521:30;10518:2;;;-1:-1;;10554:12;10518:2;;10584:78;10654:7;10645:6;10634:9;10630:22;10584:78;;;10574:88;;;9516:1162;;;;;;;;;10685:1482;;;;;;;;;;;10947:3;10935:9;10926:7;10922:23;10918:33;10915:2;;;-1:-1;;10954:12;10915:2;3717:6;3711:13;11006:74;;11117:2;11171:9;11167:22;220:13;238:33;265:5;238:33;;;11236:2;11285:22;;6616:13;11125:74;;-1:-1;6634:32;6616:13;6634:32;;;11354:2;11403:22;;6616:13;11244:73;;-1:-1;6634:32;6616:13;6634:32;;;11472:3;11522:22;;6616:13;11362:73;;-1:-1;6634:32;6616:13;6634:32;;;11591:3;11639:22;;3436:13;11481:73;;-1:-1;3454:30;3436:13;3454:30;;;11600:71;;;;11708:3;11763:9;11759:22;6476:13;11717:74;;11828:3;11883:9;11879:22;3711:13;11837:74;;11948:3;12003:9;11999:22;3711:13;11957:74;;12068:3;12123:9;12119:22;6476:13;12077:74;;10909:1258;;;;;;;;;;;;;;12174:239;;12277:2;12265:9;12256:7;12252:23;12248:32;12245:2;;;-1:-1;;12283:12;12245:2;3853:6;3840:20;3865:32;3891:5;3865:32;;12420:261;;12534:2;12522:9;12513:7;12509:23;12505:32;12502:2;;;-1:-1;;12540:12;12502:2;3992:6;3986:13;4004:32;4030:5;4004:32;;12688:655;;;;12908:2;12896:9;12887:7;12883:23;12879:32;12876:2;;;-1:-1;;12914:12;12876:2;4807:6;4794:20;4819:60;4873:5;4819:60;;;12966:90;-1:-1;13093:2;13165:22;;4597:20;4622:66;4597:20;4622:66;;;13101:96;-1:-1;13234:2;13295:22;;4980:20;5005:55;4980:20;5005:55;;;13242:85;;;;12870:473;;;;;;13350:621;;;;13510:2;13498:9;13489:7;13485:23;13481:32;13478:2;;;-1:-1;;13516:12;13478:2;5164:6;5151:20;63300:1;63293:5;63290:12;63280:2;;-1:-1;;63306:12;63280:2;13568:75;-1:-1;13680:2;13719:22;;6328:20;;-1:-1;13816:2;13801:18;;13788:32;-1:-1;;;;;13829:30;;13826:2;;;-1:-1;;13862:12;13826:2;13892:63;13947:7;13938:6;13927:9;13923:22;13892:63;;;13882:73;;;13472:499;;;;;;13978:347;;14092:2;14080:9;14071:7;14067:23;14063:32;14060:2;;;-1:-1;;14098:12;14060:2;14143:31;;-1:-1;;;;;14183:30;;14180:2;;;-1:-1;;14216:12;14180:2;14246:63;14301:7;14292:6;14281:9;14277:22;14246:63;;14332:331;;14481:2;14469:9;14460:7;14456:23;14452:32;14449:2;;;-1:-1;;14487:12;14449:2;5909:20;14481:2;5909:20;;;3442:6;3436:13;3454:30;3478:5;3454:30;;;5990:83;;6139:2;6204:22;;;6476:13;6154:16;;;6147:86;;;;-1:-1;5997:16;14443:220;-1:-1;14443:220;14670:241;;14774:2;14762:9;14753:7;14749:23;14745:32;14742:2;;;-1:-1;;14780:12;14742:2;-1:-1;6328:20;;14736:175;-1:-1;14736:175;14918:502;;;15064:2;15052:9;15043:7;15039:23;15035:32;15032:2;;;-1:-1;;15070:12;15032:2;6328:20;;;-1:-1;15250:2;15235:18;;15222:32;-1:-1;;;;;15263:30;;15260:2;;;-1:-1;;15296:12;15260:2;15326:78;15396:7;15387:6;15376:9;15372:22;15326:78;;;15316:88;;;15026:394;;;;;;15427:528;;;15584:2;15572:9;15563:7;15559:23;15555:32;15552:2;;;-1:-1;;15590:12;15552:2;6476:13;;15774:2;15759:18;;;15753:25;6476:13;;-1:-1;15774:2;-1:-1;;;;;15787:30;;15784:2;;;-1:-1;;15820:12;15784:2;15922:6;15911:9;15907:22;2628:3;2621:4;2613:6;2609:17;2605:27;2595:2;;-1:-1;;2636:12;2595:2;2676:6;2670:13;2656:27;;2698:80;2713:64;2770:6;2713:64;;2698:80;2806:21;;;2863:14;;;;2838:17;;;2952;;;2943:27;;;;2940:36;-1:-1;2937:2;;;-1:-1;;2979:12;2937:2;-1:-1;3005:10;;2999:217;3024:6;3021:1;3018:13;2999:217;;;6476:13;;3092:61;;3046:1;3039:9;;;;;3167:14;;;;3195;;2999:217;;;3003:14;15840:99;;;;;;;;15546:409;;;;;;15962:1811;;;;;;;;16318:3;16306:9;16297:7;16293:23;16289:33;16286:2;;;-1:-1;;16325:12;16286:2;6328:20;;;-1:-1;16505:2;16490:18;;16477:32;-1:-1;;;;;16518:30;;;16515:2;;;-1:-1;;16551:12;16515:2;16581:78;16651:7;16642:6;16631:9;16627:22;16581:78;;;16571:88;;16724:2;16713:9;16709:18;16696:32;16682:46;;16529:18;16740:6;16737:30;16734:2;;;-1:-1;;16770:12;16734:2;16800:78;16870:7;16861:6;16850:9;16846:22;16800:78;;;16790:88;;16943:2;16932:9;16928:18;16915:32;16901:46;;16529:18;16959:6;16956:30;16953:2;;;-1:-1;;16989:12;16953:2;17019:78;17089:7;17080:6;17069:9;17065:22;17019:78;;;17009:88;;17162:3;17151:9;17147:19;17134:33;17120:47;;16529:18;17179:6;17176:30;17173:2;;;-1:-1;;17209:12;17173:2;17239:78;17309:7;17300:6;17289:9;17285:22;17239:78;;;17229:88;;17382:3;17371:9;17367:19;17354:33;17340:47;;16529:18;17399:6;17396:30;17393:2;;;-1:-1;;17429:12;17393:2;17459:78;17529:7;17520:6;17509:9;17505:22;17459:78;;;17449:88;;17602:3;17591:9;17587:19;17574:33;17560:47;;16529:18;17619:6;17616:30;17613:2;;;-1:-1;;17649:12;17613:2;;17679:78;17749:7;17740:6;17729:9;17725:22;17679:78;;;17669:88;;;16280:1493;;;;;;;;;;;17780:535;;;;17929:2;17917:9;17908:7;17904:23;17900:32;17897:2;;;-1:-1;;17935:12;17897:2;6482:6;6476:13;17987:74;;18098:2;18152:9;18148:22;3711:13;18106:74;;18217:2;18271:9;18267:22;6476:13;18225:74;;17891:424;;;;;;19282:343;;19424:5;57518:12;57803:6;57798:3;57791:19;19517:52;19562:6;57840:4;57835:3;57831:14;57840:4;19543:5;19539:16;19517:52;;;57012:9;61842:14;-1:-1;;61838:28;19581:39;;;;57840:4;19581:39;;19372:253;-1:-1;;19372:253;35411:787;19074:37;;;35733:2;35724:12;;19074:37;;;;35835:12;;;19074:37;;;;62053:14;;;;-1:-1;;;;;;62053:14;35946:12;;;18675:58;58608:13;58601:21;61954:3;61950:15;36057:12;;;18939:52;36162:11;;;35624:574;36205:262;;19792:5;57518:12;19903:52;19948:6;19943:3;19936:4;19929:5;19925:16;19903:52;;;19967:16;;;;;36330:137;-1:-1;;36330:137;36474:427;;19792:5;57518:12;19903:52;19948:6;19943:3;19936:4;19929:5;19925:16;19903:52;;;57518:12;;;19967:16;;19903:52;57518:12;19967:16;19936:4;19925:16;;19903:52;;;19967:16;;36649:252;-1:-1;;;;36649:252;36908:213;-1:-1;;;;;59439:54;;;;18409:45;;37026:2;37011:18;;36997:124;37128:663;-1:-1;;;;;59439:54;;;18409:45;;59439:54;;37545:2;37530:18;;18409:45;37628:2;37613:18;;19074:37;;;37364:3;37665:2;37650:18;;37643:48;;;37128:663;;37705:76;;37349:19;;37767:6;37705:76;;;37697:84;37335:456;-1:-1;;;;;;37335:456;37798:201;58608:13;;58601:21;18810:34;;37910:2;37895:18;;37881:118;38006:213;19074:37;;;38124:2;38109:18;;38095:124;39050:344;39206:2;39191:18;;62166:1;62156:12;;62146:2;;62172:9;62146:2;20639:60;;;39380:2;39365:18;19074:37;39177:217;;39401:301;;39539:2;39560:17;39553:47;39614:78;39539:2;39528:9;39524:18;39678:6;39614:78;;39709:407;39900:2;39914:47;;;21657:2;39885:18;;;57791:19;21693:34;57831:14;;;21673:55;-1:-1;;;21748:12;;;21741:26;21786:12;;;39871:245;40123:407;40314:2;40328:47;;;22037:2;40299:18;;;57791:19;22073:34;57831:14;;;22053:55;-1:-1;;;22128:12;;;22121:42;22182:12;;;40285:245;40537:407;40728:2;40742:47;;;22433:2;40713:18;;;57791:19;22469:34;57831:14;;;22449:55;-1:-1;;;22524:12;;;22517:33;22569:12;;;40699:245;40951:407;41142:2;41156:47;;;22820:2;41127:18;;;57791:19;-1:-1;;;57831:14;;;22836:51;22906:12;;;41113:245;41365:407;41556:2;41570:47;;;23157:2;41541:18;;;57791:19;23193:34;57831:14;;;23173:55;-1:-1;;;23248:12;;;23241:25;23285:12;;;41527:245;41779:407;41970:2;41984:47;;;23536:2;41955:18;;;57791:19;23572:34;57831:14;;;23552:55;-1:-1;;;23627:12;;;23620:28;23667:12;;;41941:245;42193:407;42384:2;42398:47;;;23918:2;42369:18;;;57791:19;-1:-1;;;57831:14;;;23934:48;24001:12;;;42355:245;42607:407;42798:2;42812:47;;;24252:2;42783:18;;;57791:19;24288:34;57831:14;;;24268:55;-1:-1;;;24343:12;;;24336:28;24383:12;;;42769:245;43435:407;43626:2;43640:47;;;25018:2;43611:18;;;57791:19;25054:34;57831:14;;;25034:55;-1:-1;;;25109:12;;;25102:36;25157:12;;;43597:245;43849:407;44040:2;44054:47;;;25408:2;44025:18;;;57791:19;25444:32;57831:14;;;25424:53;25496:12;;;44011:245;44263:407;44454:2;44468:47;;;25747:2;44439:18;;;57791:19;25783:34;57831:14;;;25763:55;-1:-1;;;25838:12;;;25831:34;25884:12;;;44425:245;44677:407;44868:2;44882:47;;;26135:2;44853:18;;;57791:19;26171:34;57831:14;;;26151:55;-1:-1;;;26226:12;;;26219:48;26286:12;;;44839:245;45091:407;45282:2;45296:47;;;26537:2;45267:18;;;57791:19;26573:34;57831:14;;;26553:55;-1:-1;;;26628:12;;;26621:34;26674:12;;;45253:245;45505:407;45696:2;45710:47;;;26925:2;45681:18;;;57791:19;-1:-1;;;57831:14;;;26941:42;27002:12;;;45667:245;45919:407;46110:2;46124:47;;;27253:2;46095:18;;;57791:19;27289:34;57831:14;;;27269:55;-1:-1;;;27344:12;;;27337:26;27382:12;;;46081:245;46333:407;46524:2;46538:47;;;46509:18;;;57791:19;27669:34;57831:14;;;27649:55;27723:12;;;46495:245;46747:407;46938:2;46952:47;;;27974:2;46923:18;;;57791:19;-1:-1;;;57831:14;;;27990:51;28060:12;;;46909:245;47161:407;47352:2;47366:47;;;28311:2;47337:18;;;57791:19;28347:34;57831:14;;;28327:55;-1:-1;;;28402:12;;;28395:37;28451:12;;;47323:245;47575:407;47766:2;47780:47;;;28702:2;47751:18;;;57791:19;28738:34;57831:14;;;28718:55;-1:-1;;;28793:12;;;28786:40;28845:12;;;47737:245;47989:407;48180:2;48194:47;;;29096:2;48165:18;;;57791:19;29132:34;57831:14;;;29112:55;-1:-1;;;29187:12;;;29180:36;29235:12;;;48151:245;48403:407;48594:2;48608:47;;;29486:2;48579:18;;;57791:19;29522:34;57831:14;;;29502:55;-1:-1;;;29577:12;;;29570:33;29622:12;;;48565:245;48817:407;49008:2;49022:47;;;29873:2;48993:18;;;57791:19;29909:34;57831:14;;;29889:55;-1:-1;;;29964:12;;;29957:39;30015:12;;;48979:245;49231:407;49422:2;49436:47;;;30266:2;49407:18;;;57791:19;-1:-1;;;57831:14;;;30282:49;30350:12;;;49393:245;49645:407;49836:2;49850:47;;;30601:2;49821:18;;;57791:19;-1:-1;;;57831:14;;;30617:50;30686:12;;;49807:245;50059:407;50250:2;50264:47;;;30937:2;50235:18;;;57791:19;-1:-1;;;57831:14;;;30953:49;31021:12;;;50221:245;50473:407;50664:2;50678:47;;;31272:2;50649:18;;;57791:19;31308:34;57831:14;;;31288:55;-1:-1;;;31363:12;;;31356:27;31402:12;;;50635:245;50887:407;51078:2;51092:47;;;31653:2;51063:18;;;57791:19;31689:34;57831:14;;;31669:55;-1:-1;;;31744:12;;;31737:25;31781:12;;;51049:245;51301:407;51492:2;51506:47;;;32032:2;51477:18;;;57791:19;32068:34;57831:14;;;32048:55;-1:-1;;;32123:12;;;32116:30;32165:12;;;51463:245;51715:407;51906:2;51920:47;;;32416:2;51891:18;;;57791:19;-1:-1;;;57831:14;;;32432:42;32493:12;;;51877:245;52129:407;52320:2;52334:47;;;32744:2;52305:18;;;57791:19;32780:34;57831:14;;;32760:55;-1:-1;;;32835:12;;;32828:41;32888:12;;;52291:245;52543:407;52734:2;52748:47;;;33139:2;52719:18;;;57791:19;-1:-1;;;57831:14;;;33155:50;33224:12;;;52705:245;52957:407;53148:2;53162:47;;;33475:2;53133:18;;;57791:19;33511:31;57831:14;;;33491:52;33562:12;;;53119:245;53371:407;53562:2;53576:47;;;33813:2;53547:18;;;57791:19;33849:34;57831:14;;;33829:55;-1:-1;;;33904:12;;;33897:28;33944:12;;;53533:245;53785:407;53976:2;53990:47;;;34195:2;53961:18;;;57791:19;34231:34;57831:14;;;34211:55;-1:-1;;;34286:12;;;34279:33;34331:12;;;53947:245;54199:407;54390:2;54404:47;;;34582:2;54375:18;;;57791:19;34618:34;57831:14;;;34598:55;-1:-1;;;34673:12;;;34666:39;34724:12;;;54361:245;54613:407;54804:2;54818:47;;;34975:2;54789:18;;;57791:19;35011:34;57831:14;;;34991:55;-1:-1;;;35066:12;;;35059:28;35106:12;;;54775:245;55247:324;19074:37;;;-1:-1;;;;;59439:54;55557:2;55542:18;;18409:45;55393:2;55378:18;;55364:207;55578:256;55640:2;55634:9;55666:17;;;-1:-1;;;;;55726:34;;55762:22;;;55723:62;55720:2;;;55798:1;;55788:12;55720:2;55640;55807:22;55618:216;;-1:-1;55618:216;55841:304;;-1:-1;;;;;55989:30;;55986:2;;;-1:-1;;56022:12;55986:2;-1:-1;56067:4;56055:17;;;56120:15;;55923:222;60946:268;61011:1;61018:101;61032:6;61029:1;61026:13;61018:101;;;61099:11;;;61093:18;61080:11;;;61073:39;61054:2;61047:10;61018:101;;;61134:6;61131:1;61128:13;61125:2;;;-1:-1;;61011:1;61181:16;;61174:27;60995:219;62195:117;-1:-1;;;;;59439:54;;62254:35;;62244:2;;62303:1;;62293:12;62319:111;62400:5;58608:13;58601:21;62378:5;62375:32;62365:2;;62421:1;;62411:12;62561:115;-1:-1;;;;;;58774:78;;62619:34;;62609:2;;62667:1;;62657:12;63456:115;59656:10;63541:5;59645:22;63517:5;63514:34;63504:2;;63562:1;;63552:12

Swarm Source

ipfs://e731d2d277243cb26ce67dd65f8de00b43380526a9a0457ad6a480b5a9a8b173

Block Transaction Gas Used Reward
view all blocks collator

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

Validator Index Block Amount
View All Withdrawals

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