ETH Price: $2,153.88 (+0.58%)

Transaction Decoder

Block:
21168418 at Nov-12-2024 01:47:11 AM +UTC
Transaction Fee:
0.005707495427142509 ETH $12.29
Gas Used:
260,939 Gas / 21.872910631 Gwei

Emitted Events:

746 TellorMaster.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000008cfc184c877154a8f9ffe0fe75649dbe5e2dbebf, 0x0000000000000000000000005670d6fbf43877470a5e1295267adfb741408eb7, 00000000000000000000000000000000000000000000000000470de4df820000 )
747 TellorFlex.NewReport( _queryId=0BC2D41117AE8779DA7623EE76A109C88B84B9BF4D9B404524DF04F7D0CA4CA7, _time=1731376031, _value=0x0000000000000000000000000000000000000000000000C97FAE5C296BBBBCCB, _nonce=1154, _queryData=0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000953706F745072696365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004726574680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037573640000000000000000000000000000000000000000000000000000000000, _reporter=[Sender] 0x5670d6fbf43877470a5e1295267adfb741408eb7 )

Account State Difference:

  Address   Before After State Difference Code
0x5670D6FB...741408EB7
13.400710171807284218 Eth
Nonce: 5907
13.395002676380141709 Eth
Nonce: 5908
0.005707495427142509
0x88dF592F...2cf3778a0
0x8cFc184c...e5e2DBEbf
(Tellor: Oracle Contract)
(beaverbuild)
12.960959558564085859 Eth12.960960277201051297 Eth0.000000718636965438

Execution Trace

TellorFlex.submitValue( _queryId=0BC2D41117AE8779DA7623EE76A109C88B84B9BF4D9B404524DF04F7D0CA4CA7, _value=0x0000000000000000000000000000000000000000000000C97FAE5C296BBBBCCB, _nonce=1154, _queryData=0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000953706F745072696365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004726574680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037573640000000000000000000000000000000000000000000000000000000000 )
  • TellorMaster.70a08231( )
    • Tellor360.balanceOf( _user=0x8cFc184c877154a8F9ffE0fe75649dbe5e2DBEbf ) => ( 38009818061889876787055 )
    • TellorMaster.a9059cbb( )
      • Tellor360.transfer( _to=0x5670D6FBf43877470A5E1295267Adfb741408EB7, _amount=20000000000000000 ) => ( success=True )
        File 1 of 3: TellorFlex
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.3;
        interface IERC20 {
            function balanceOf(address account) external view returns (uint256);
            function transfer(address recipient, uint256 amount)
                external
                returns (bool);
            function transferFrom(
                address sender,
                address recipient,
                uint256 amount
            ) external returns (bool);
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.3;
        import "./interfaces/IERC20.sol";
        /**
         @author Tellor Inc.
         @title TellorFlex
         @dev This is a streamlined Tellor oracle system which handles staking, reporting,
         * slashing, and user data getters in one contract. This contract is controlled
         * by a single address known as 'governance', which could be an externally owned
         * account or a contract, allowing for a flexible, modular design.
        */
        contract TellorFlex {
            // Storage
            IERC20 public immutable token; // token used for staking and rewards
            address public governance; // address with ability to remove values and slash reporters
            address public immutable owner; // contract deployer, can call init function once
            uint256 public accumulatedRewardPerShare; // accumulated staking reward per staked token
            uint256 public immutable minimumStakeAmount; // minimum amount of tokens required to stake
            uint256 public immutable reportingLock; // base amount of time before a reporter is able to submit a value again
            uint256 public rewardRate; // total staking rewards released per second
            uint256 public stakeAmount; // minimum amount required to be a staker
            uint256 public immutable stakeAmountDollarTarget; // amount of US dollars required to be a staker
            uint256 public stakingRewardsBalance; // total amount of staking rewards
            bytes32 public immutable stakingTokenPriceQueryId; // staking token SpotPrice queryId, used for updating stakeAmount
            uint256 public constant timeBasedReward = 5e17; // amount of TB rewards released per 5 minutes
            uint256 public timeOfLastAllocation; // time of last update to accumulatedRewardPerShare
            uint256 public timeOfLastNewValue = block.timestamp; // time of the last new submitted value, originally set to the block timestamp
            uint256 public totalRewardDebt; // staking reward debt, used to calculate real staking rewards balance
            uint256 public totalStakeAmount; // total amount of tokens locked in contract (via stake)
            uint256 public totalStakers; // total number of stakers with at least stakeAmount staked, not exact
            uint256 public toWithdraw; //amountLockedForWithdrawal
            mapping(bytes32 => Report) private reports; // mapping of query IDs to a report
            mapping(address => StakeInfo) private stakerDetails; // mapping from a persons address to their staking info
            // Structs
            struct Report {
                uint256[] timestamps; // array of all newValueTimestamps reported
                mapping(uint256 => uint256) timestampIndex; // mapping of timestamps to respective indices
                mapping(uint256 => bytes) valueByTimestamp; // mapping of timestamps to values
                mapping(uint256 => address) reporterByTimestamp; // mapping of timestamps to reporters
                mapping(uint256 => bool) isDisputed;
            }
            struct StakeInfo {
                uint256 startDate; // stake or withdrawal request start date
                uint256 stakedBalance; // staked token balance
                uint256 lockedBalance; // amount locked for withdrawal
                uint256 rewardDebt; // used for staking reward calculation
                uint256 reporterLastTimestamp; // timestamp of reporter's last reported value
                uint256 reportsSubmitted; // total number of reports submitted by reporter
                uint256 startVoteCount; // total number of governance votes when stake deposited
                uint256 startVoteTally; // staker vote tally when stake deposited
                bool staked; // used to keep track of total stakers
            }
            // Events
            event NewReport(
                bytes32 indexed _queryId,
                uint256 indexed _time,
                bytes _value,
                uint256 _nonce,
                bytes _queryData,
                address indexed _reporter
            );
            event NewStakeAmount(uint256 _newStakeAmount);
            event NewStaker(address indexed _staker, uint256 indexed _amount);
            event ReporterSlashed(
                address indexed _reporter,
                address _recipient,
                uint256 _slashAmount
            );
            event StakeWithdrawn(address _staker);
            event StakeWithdrawRequested(address _staker, uint256 _amount);
            event ValueRemoved(bytes32 _queryId, uint256 _timestamp);
            // Functions
            /**
             * @dev Initializes system parameters
             * @param _token address of token used for staking and rewards
             * @param _reportingLock base amount of time (seconds) before reporter is able to report again
             * @param _stakeAmountDollarTarget fixed USD amount that stakeAmount targets on updateStakeAmount
             * @param _stakingTokenPrice current price of staking token in USD (18 decimals)
             * @param _stakingTokenPriceQueryId queryId where staking token price is reported
             */
            constructor(
                address _token,
                uint256 _reportingLock,
                uint256 _stakeAmountDollarTarget,
                uint256 _stakingTokenPrice,
                uint256 _minimumStakeAmount,
                bytes32 _stakingTokenPriceQueryId
            ) {
                require(_token != address(0), "must set token address");
                require(_stakingTokenPrice > 0, "must set staking token price");
                require(_reportingLock > 0, "must set reporting lock");
                require(_stakingTokenPriceQueryId != bytes32(0), "must set staking token price queryId");
                token = IERC20(_token);
                owner = msg.sender;
                reportingLock = _reportingLock;
                stakeAmountDollarTarget = _stakeAmountDollarTarget;
                minimumStakeAmount = _minimumStakeAmount;
                uint256 _potentialStakeAmount = (_stakeAmountDollarTarget * 1e18) / _stakingTokenPrice;
                if(_potentialStakeAmount < _minimumStakeAmount) {
                    stakeAmount = _minimumStakeAmount;
                } else {
                    stakeAmount = _potentialStakeAmount;
                }
                stakingTokenPriceQueryId = _stakingTokenPriceQueryId;
            }
            /**
             * @dev Allows the owner to initialize the governance (flex addy needed for governance deployment)
             * @param _governanceAddress address of governance contract (github.com/tellor-io/governance)
             */
            function init(address _governanceAddress) external {
                require(msg.sender == owner, "only owner can set governance address");
                require(governance == address(0), "governance address already set");
                require(
                    _governanceAddress != address(0),
                    "governance address can't be zero address"
                );
                governance = _governanceAddress;
            }
            /**
             * @dev Funds the Flex contract with staking rewards (paid by autopay and minting)
             * @param _amount amount of tokens to fund contract with
             */
            function addStakingRewards(uint256 _amount) external {
                require(token.transferFrom(msg.sender, address(this), _amount));
                _updateRewards();
                stakingRewardsBalance += _amount;
                // update reward rate = real staking rewards balance / 30 days
                rewardRate =
                    (stakingRewardsBalance -
                        ((accumulatedRewardPerShare * totalStakeAmount) /
                            1e18 -
                            totalRewardDebt)) /
                    30 days;
            }
            /**
             * @dev Allows a reporter to submit stake
             * @param _amount amount of tokens to stake
             */
            function depositStake(uint256 _amount) external {
                require(governance != address(0), "governance address not set");
                StakeInfo storage _staker = stakerDetails[msg.sender];
                uint256 _stakedBalance = _staker.stakedBalance;
                uint256 _lockedBalance = _staker.lockedBalance;
                if (_lockedBalance > 0) {
                    if (_lockedBalance >= _amount) {
                        // if staker's locked balance covers full _amount, use that
                        _staker.lockedBalance -= _amount;
                        toWithdraw -= _amount;
                    } else {
                        // otherwise, stake the whole locked balance and transfer the
                        // remaining amount from the staker's address
                        require(
                            token.transferFrom(
                                msg.sender,
                                address(this),
                                _amount - _lockedBalance
                            )
                        );
                        toWithdraw -= _staker.lockedBalance;
                        _staker.lockedBalance = 0;
                    }
                } else {
                    if (_stakedBalance == 0) {
                        // if staked balance and locked balance equal 0, save current vote tally.
                        // voting participation used for calculating rewards
                        (bool _success, bytes memory _returnData) = governance.call(
                            abi.encodeWithSignature("getVoteCount()")
                        );
                        if (_success) {
                            _staker.startVoteCount = uint256(abi.decode(_returnData, (uint256)));
                        }
                        (_success,_returnData) = governance.call(
                            abi.encodeWithSignature("getVoteTallyByAddress(address)",msg.sender)
                        );
                        if(_success){
                            _staker.startVoteTally =  abi.decode(_returnData,(uint256));
                        }
                    }
                    require(token.transferFrom(msg.sender, address(this), _amount));
                }
                _updateStakeAndPayRewards(msg.sender, _stakedBalance + _amount);
                _staker.startDate = block.timestamp; // This resets the staker start date to now
                emit NewStaker(msg.sender, _amount);
            }
            /**
             * @dev Removes a value from the oracle.
             * Note: this function is only callable by the Governance contract.
             * @param _queryId is ID of the specific data feed
             * @param _timestamp is the timestamp of the data value to remove
             */
            function removeValue(bytes32 _queryId, uint256 _timestamp) external {
                require(msg.sender == governance, "caller must be governance address");
                Report storage _report = reports[_queryId];
                require(!_report.isDisputed[_timestamp], "value already disputed");
                uint256 _index = _report.timestampIndex[_timestamp];
                require(_timestamp == _report.timestamps[_index], "invalid timestamp");
                _report.valueByTimestamp[_timestamp] = "";
                _report.isDisputed[_timestamp] = true;
                emit ValueRemoved(_queryId, _timestamp);
            }
            /**
             * @dev Allows a reporter to request to withdraw their stake
             * @param _amount amount of staked tokens requesting to withdraw
             */
            function requestStakingWithdraw(uint256 _amount) external {
                StakeInfo storage _staker = stakerDetails[msg.sender];
                require(
                    _staker.stakedBalance >= _amount,
                    "insufficient staked balance"
                );
                _updateStakeAndPayRewards(msg.sender, _staker.stakedBalance - _amount);
                _staker.startDate = block.timestamp;
                _staker.lockedBalance += _amount;
                toWithdraw += _amount;
                emit StakeWithdrawRequested(msg.sender, _amount);
            }
            /**
             * @dev Slashes a reporter and transfers their stake amount to the given recipient
             * Note: this function is only callable by the governance address.
             * @param _reporter is the address of the reporter being slashed
             * @param _recipient is the address receiving the reporter's stake
             * @return _slashAmount uint256 amount of token slashed and sent to recipient address
             */
            function slashReporter(address _reporter, address _recipient)
                external
                returns (uint256 _slashAmount)
            {
                require(msg.sender == governance, "only governance can slash reporter");
                StakeInfo storage _staker = stakerDetails[_reporter];
                uint256 _stakedBalance = _staker.stakedBalance;
                uint256 _lockedBalance = _staker.lockedBalance;
                require(_stakedBalance + _lockedBalance > 0, "zero staker balance");
                if (_lockedBalance >= stakeAmount) {
                    // if locked balance is at least stakeAmount, slash from locked balance
                    _slashAmount = stakeAmount;
                    _staker.lockedBalance -= stakeAmount;
                    toWithdraw -= stakeAmount;
                } else if (_lockedBalance + _stakedBalance >= stakeAmount) {
                    // if locked balance + staked balance is at least stakeAmount,
                    // slash from locked balance and slash remainder from staked balance
                    _slashAmount = stakeAmount;
                    _updateStakeAndPayRewards(
                        _reporter,
                        _stakedBalance - (stakeAmount - _lockedBalance)
                    );
                    toWithdraw -= _lockedBalance;
                    _staker.lockedBalance = 0;
                } else {
                    // if sum(locked balance + staked balance) is less than stakeAmount,
                    // slash sum
                    _slashAmount = _stakedBalance + _lockedBalance;
                    toWithdraw -= _lockedBalance;
                    _updateStakeAndPayRewards(_reporter, 0);
                    _staker.lockedBalance = 0;
                }
                require(token.transfer(_recipient, _slashAmount));
                emit ReporterSlashed(_reporter, _recipient, _slashAmount);
            }
            /**
             * @dev Allows a reporter to submit a value to the oracle
             * @param _queryId is ID of the specific data feed. Equals keccak256(_queryData) for non-legacy IDs
             * @param _value is the value the user submits to the oracle
             * @param _nonce is the current value count for the query id
             * @param _queryData is the data used to fulfill the data query
             */
            function submitValue(
                bytes32 _queryId,
                bytes calldata _value,
                uint256 _nonce,
                bytes calldata _queryData
            ) external {
                require(keccak256(_value) != keccak256(""), "value must be submitted");
                Report storage _report = reports[_queryId];
                require(
                    _nonce == _report.timestamps.length || _nonce == 0,
                    "nonce must match timestamp index"
                );
                StakeInfo storage _staker = stakerDetails[msg.sender];
                require(
                    _staker.stakedBalance >= stakeAmount,
                    "balance must be greater than stake amount"
                );
                // Require reporter to abide by given reporting lock
                require(
                    (block.timestamp - _staker.reporterLastTimestamp) * 1000 >
                        (reportingLock * 1000) / (_staker.stakedBalance / stakeAmount),
                    "still in reporter time lock, please wait!"
                );
                require(
                    _queryId == keccak256(_queryData),
                    "query id must be hash of query data"
                );
                _staker.reporterLastTimestamp = block.timestamp;
                // Checks for no double reporting of timestamps
                require(
                    _report.reporterByTimestamp[block.timestamp] == address(0),
                    "timestamp already reported for"
                );
                // Update number of timestamps, value for given timestamp, and reporter for timestamp
                _report.timestampIndex[block.timestamp] = _report.timestamps.length;
                _report.timestamps.push(block.timestamp);
                _report.valueByTimestamp[block.timestamp] = _value;
                _report.reporterByTimestamp[block.timestamp] = msg.sender;
                // Disperse Time Based Reward
                uint256 _reward = ((block.timestamp - timeOfLastNewValue) * timeBasedReward) / 300; //.5 TRB per 5 minutes
                uint256 _totalTimeBasedRewardsBalance =
                    token.balanceOf(address(this)) -
                    (totalStakeAmount + stakingRewardsBalance + toWithdraw);
                if (_totalTimeBasedRewardsBalance > 0 && _reward > 0) {
                    if (_totalTimeBasedRewardsBalance < _reward) {
                        token.transfer(msg.sender, _totalTimeBasedRewardsBalance);
                    } else {
                        token.transfer(msg.sender, _reward);
                    }
                }
                // Update last oracle value and number of values submitted by a reporter
                timeOfLastNewValue = block.timestamp;
                unchecked{
                    _staker.reportsSubmitted++;
                }
                emit NewReport(
                    _queryId,
                    block.timestamp,
                    _value,
                    _nonce,
                    _queryData,
                    msg.sender
                );
            }
            /**
             * @dev Updates the stake amount after retrieving the latest
             * 12+-hour-old staking token price from the oracle
             */
            function updateStakeAmount() external {
                // get staking token price
                (bool _valFound, bytes memory _val, ) = getDataBefore(
                    stakingTokenPriceQueryId,
                    block.timestamp - 12 hours
                );
                if (_valFound) {
                    uint256 _stakingTokenPrice = abi.decode(_val, (uint256));
                    require(
                        _stakingTokenPrice >= 0.01 ether && _stakingTokenPrice < 1000000 ether,
                        "invalid staking token price"
                    );
                    uint256 _adjustedStakeAmount = (stakeAmountDollarTarget * 1e18) / _stakingTokenPrice;
                    if(_adjustedStakeAmount < minimumStakeAmount) {
                        stakeAmount = minimumStakeAmount;
                    } else {
                        stakeAmount = _adjustedStakeAmount;
                    }
                    emit NewStakeAmount(stakeAmount);
                }
            }
            /**
             * @dev Withdraws a reporter's stake after the lock period expires
             */
            function withdrawStake() external {
                StakeInfo storage _staker = stakerDetails[msg.sender];
                // Ensure reporter is locked and that enough time has passed
                require(
                    block.timestamp - _staker.startDate >= 7 days,
                    "7 days didn't pass"
                );
                require(
                    _staker.lockedBalance > 0,
                    "reporter not locked for withdrawal"
                );
                require(token.transfer(msg.sender, _staker.lockedBalance));
                toWithdraw -= _staker.lockedBalance;
                _staker.lockedBalance = 0;
                emit StakeWithdrawn(msg.sender);
            }
            // *****************************************************************************
            // *                                                                           *
            // *                               Getters                                     *
            // *                                                                           *
            // *****************************************************************************
            /**
             * @dev Returns the current value of a data feed given a specific ID
             * @param _queryId is the ID of the specific data feed
             * @return _value the latest submitted value for the given queryId
             */
            function getCurrentValue(bytes32 _queryId)
                external
                view
                returns (bytes memory _value)
            {
                bool _didGet;
                (_didGet, _value, ) = getDataBefore(_queryId, block.timestamp + 1);
                if(!_didGet){revert();}
            }
            /**
             * @dev Retrieves the latest value for the queryId before the specified timestamp
             * @param _queryId is the queryId to look up the value for
             * @param _timestamp before which to search for latest value
             * @return _ifRetrieve bool true if able to retrieve a non-zero value
             * @return _value the value retrieved
             * @return _timestampRetrieved the value's timestamp
             */
            function getDataBefore(bytes32 _queryId, uint256 _timestamp)
                public
                view
                returns (
                    bool _ifRetrieve,
                    bytes memory _value,
                    uint256 _timestampRetrieved
                )
            {
                (bool _found, uint256 _index) = getIndexForDataBefore(
                    _queryId,
                    _timestamp
                );
                if (!_found) return (false, bytes(""), 0);
                _timestampRetrieved = getTimestampbyQueryIdandIndex(_queryId, _index);
                _value = retrieveData(_queryId, _timestampRetrieved);
                return (true, _value, _timestampRetrieved);
            }
            /**
             * @dev Returns governance address
             * @return address governance
             */
            function getGovernanceAddress() external view returns (address) {
                return governance;
            }
            /**
             * @dev Counts the number of values that have been submitted for the request.
             * @param _queryId the id to look up
             * @return uint256 count of the number of values received for the id
             */
            function getNewValueCountbyQueryId(bytes32 _queryId)
                public
                view
                returns (uint256)
            {
                return reports[_queryId].timestamps.length;
            }
            /**
             * @dev Returns the pending staking reward for a given address
             * @param _stakerAddress staker address to look up
             * @return _pendingReward - pending reward for given staker
             */
            function getPendingRewardByStaker(address _stakerAddress)
                external
                returns (uint256 _pendingReward)
            {
                StakeInfo storage _staker = stakerDetails[_stakerAddress];
                _pendingReward = (_staker.stakedBalance *
                    _getUpdatedAccumulatedRewardPerShare()) /
                    1e18 -
                    _staker.rewardDebt;
                (bool _success, bytes memory _returnData) = governance.call(
                    abi.encodeWithSignature("getVoteCount()")
                );
                uint256 _numberOfVotes;
                if (_success) {
                        _numberOfVotes = uint256(abi.decode(_returnData, (uint256))) - _staker.startVoteCount;
                }
                if (_numberOfVotes > 0) {
                        (_success,_returnData) = governance.call(
                            abi.encodeWithSignature("getVoteTallyByAddress(address)",_stakerAddress)
                        );
                        if(_success){
                            _pendingReward =
                                (_pendingReward * (abi.decode(_returnData,(uint256)) - _staker.startVoteTally)) 
                                / _numberOfVotes;
                        }
                }
            }
            /**
             * @dev Returns the real staking rewards balance after accounting for unclaimed rewards
             * @return uint256 real staking rewards balance
             */
            function getRealStakingRewardsBalance() external view returns (uint256) {
                uint256 _pendingRewards = (_getUpdatedAccumulatedRewardPerShare() *
                    totalStakeAmount) /
                    1e18 -
                    totalRewardDebt;
                return (stakingRewardsBalance - _pendingRewards);
            }
            /**
             * @dev Returns reporter address and whether a value was removed for a given queryId and timestamp
             * @param _queryId the id to look up
             * @param _timestamp is the timestamp of the value to look up
             * @return address reporter who submitted the value
             * @return bool true if the value was removed
             */
            function getReportDetails(bytes32 _queryId, uint256 _timestamp)
                external
                view
                returns (address, bool)
            {
                return (reports[_queryId].reporterByTimestamp[_timestamp], reports[_queryId].isDisputed[_timestamp]);
            }
            /**
             * @dev Returns the address of the reporter who submitted a value for a data ID at a specific time
             * @param _queryId is ID of the specific data feed
             * @param _timestamp is the timestamp to find a corresponding reporter for
             * @return address of the reporter who reported the value for the data ID at the given timestamp
             */
            function getReporterByTimestamp(bytes32 _queryId, uint256 _timestamp)
                external
                view
                returns (address)
            {
                return reports[_queryId].reporterByTimestamp[_timestamp];
            }
            /**
             * @dev Returns the timestamp of the reporter's last submission
             * @param _reporter is address of the reporter
             * @return uint256 timestamp of the reporter's last submission
             */
            function getReporterLastTimestamp(address _reporter)
                external
                view
                returns (uint256)
            {
                return stakerDetails[_reporter].reporterLastTimestamp;
            }
            /**
             * @dev Returns the reporting lock time, the amount of time a reporter must wait to submit again
             * @return uint256 reporting lock time
             */
            function getReportingLock() external view returns (uint256) {
                return reportingLock;
            }
            /**
             * @dev Returns the number of values submitted by a specific reporter address
             * @param _reporter is the address of a reporter
             * @return uint256 the number of values submitted by the given reporter
             */
            function getReportsSubmittedByAddress(address _reporter)
                external
                view
                returns (uint256)
            {
                return stakerDetails[_reporter].reportsSubmitted;
            }
            /**
             * @dev Returns amount required to report oracle values
             * @return uint256 stake amount
             */
            function getStakeAmount() external view returns (uint256) {
                return stakeAmount;
            }
            /**
             * @dev Returns all information about a staker
             * @param _stakerAddress address of staker inquiring about
             * @return uint startDate of staking
             * @return uint current amount staked
             * @return uint current amount locked for withdrawal
             * @return uint reward debt used to calculate staking rewards
             * @return uint reporter's last reported timestamp
             * @return uint total number of reports submitted by reporter
             * @return uint governance vote count when first staked
             * @return uint number of votes cast by staker when first staked
             * @return bool whether staker is counted in totalStakers
             */
            function getStakerInfo(address _stakerAddress)
                external
                view
                returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    bool
                )
            {
                StakeInfo storage _staker = stakerDetails[_stakerAddress];
                return (
                    _staker.startDate,
                    _staker.stakedBalance,
                    _staker.lockedBalance,
                    _staker.rewardDebt,
                    _staker.reporterLastTimestamp,
                    _staker.reportsSubmitted,
                    _staker.startVoteCount,
                    _staker.startVoteTally,
                    _staker.staked
                );
            }
            /**
             * @dev Returns the timestamp for the last value of any ID from the oracle
             * @return uint256 timestamp of the last oracle value
             */
            function getTimeOfLastNewValue() external view returns (uint256) {
                return timeOfLastNewValue;
            }
            /**
             * @dev Gets the timestamp for the value based on their index
             * @param _queryId is the id to look up
             * @param _index is the value index to look up
             * @return uint256 timestamp
             */
            function getTimestampbyQueryIdandIndex(bytes32 _queryId, uint256 _index)
                public
                view
                returns (uint256)
            {
                return reports[_queryId].timestamps[_index];
            }
            /**
             * @dev Retrieves latest array index of data before the specified timestamp for the queryId
             * @param _queryId is the queryId to look up the index for
             * @param _timestamp is the timestamp before which to search for the latest index
             * @return _found whether the index was found
             * @return _index the latest index found before the specified timestamp
             */
            // slither-disable-next-line calls-loop
            function getIndexForDataBefore(bytes32 _queryId, uint256 _timestamp)
                public
                view
                returns (bool _found, uint256 _index)
            {
                uint256 _count = getNewValueCountbyQueryId(_queryId);
                if (_count > 0) {
                    uint256 _middle;
                    uint256 _start = 0;
                    uint256 _end = _count - 1;
                    uint256 _time;
                    //Checking Boundaries to short-circuit the algorithm
                    _time = getTimestampbyQueryIdandIndex(_queryId, _start);
                    if (_time >= _timestamp) return (false, 0);
                    _time = getTimestampbyQueryIdandIndex(_queryId, _end);
                    if (_time < _timestamp) {
                        while(isInDispute(_queryId, _time) && _end > 0) {
                            _end--;
                            _time = getTimestampbyQueryIdandIndex(_queryId, _end);
                        }
                        if(_end == 0 && isInDispute(_queryId, _time)) {
                            return (false, 0);
                        }
                        return (true, _end);
                    }
                    //Since the value is within our boundaries, do a binary search
                    while (true) {
                        _middle = (_end - _start) / 2 + 1 + _start;
                        _time = getTimestampbyQueryIdandIndex(_queryId, _middle);
                        if (_time < _timestamp) {
                            //get immediate next value
                            uint256 _nextTime = getTimestampbyQueryIdandIndex(
                                _queryId,
                                _middle + 1
                            );
                            if (_nextTime >= _timestamp) {
                                if(!isInDispute(_queryId, _time)) {
                                    // _time is correct
                                    return (true, _middle);
                                } else {
                                    // iterate backwards until we find a non-disputed value
                                    while(isInDispute(_queryId, _time) && _middle > 0) {
                                        _middle--;
                                        _time = getTimestampbyQueryIdandIndex(_queryId, _middle);
                                    }
                                    if(_middle == 0 && isInDispute(_queryId, _time)) {
                                        return (false, 0);
                                    }
                                    // _time is correct
                                    return (true, _middle);
                                }
                            } else {
                                //look from middle + 1(next value) to end
                                _start = _middle + 1;
                            }
                        } else {
                            uint256 _prevTime = getTimestampbyQueryIdandIndex(
                                _queryId,
                                _middle - 1
                            );
                            if (_prevTime < _timestamp) {
                                if(!isInDispute(_queryId, _prevTime)) {
                                    // _prevTime is correct
                                    return (true, _middle - 1);
                                } else {
                                    // iterate backwards until we find a non-disputed value
                                    _middle--;
                                    while(isInDispute(_queryId, _prevTime) && _middle > 0) {
                                        _middle--;
                                        _prevTime = getTimestampbyQueryIdandIndex(
                                            _queryId,
                                            _middle
                                        );
                                    }
                                    if(_middle == 0 && isInDispute(_queryId, _prevTime)) {
                                        return (false, 0);
                                    }
                                    // _prevtime is correct
                                    return (true, _middle);
                                }
                            } else {
                                //look from start to middle -1(prev value)
                                _end = _middle - 1;
                            }
                        }
                    }
                }
                return (false, 0);
            }
            /**
             * @dev Returns the index of a reporter timestamp in the timestamp array for a specific data ID
             * @param _queryId is ID of the specific data feed
             * @param _timestamp is the timestamp to find in the timestamps array
             * @return uint256 of the index of the reporter timestamp in the array for specific ID
             */
            function getTimestampIndexByTimestamp(bytes32 _queryId, uint256 _timestamp)
                external
                view
                returns (uint256)
            {
                return reports[_queryId].timestampIndex[_timestamp];
            }
            /**
             * @dev Returns the address of the token used for staking
             * @return address of the token used for staking
             */
            function getTokenAddress() external view returns (address) {
                return address(token);
            }
            /**
             * @dev Returns total amount of token staked for reporting
             * @return uint256 total amount of token staked
             */
            function getTotalStakeAmount() external view returns (uint256) {
                return totalStakeAmount;
            }
            /**
             * @dev Returns total number of current stakers. Reporters with stakedBalance less than stakeAmount are excluded from this total
             * @return uint256 total stakers
             */
            function getTotalStakers() external view returns (uint256) {
                return totalStakers;
            }
            /**
             * @dev Returns total balance of time based rewards in contract
             * @return uint256 amount of trb
             */
            function getTotalTimeBasedRewardsBalance() external view returns (uint256) {
                return token.balanceOf(address(this)) - (totalStakeAmount + stakingRewardsBalance + toWithdraw);
            }
            /**
             * @dev Returns whether a given value is disputed
             * @param _queryId unique ID of the data feed
             * @param _timestamp timestamp of the value
             * @return bool whether the value is disputed
             */
            function isInDispute(bytes32 _queryId, uint256 _timestamp)
                public
                view
                returns (bool)
            {
                return reports[_queryId].isDisputed[_timestamp];
            }
            /**
             * @dev Retrieve value from oracle based on timestamp
             * @param _queryId being requested
             * @param _timestamp to retrieve data/value from
             * @return bytes value for timestamp submitted
             */
            function retrieveData(bytes32 _queryId, uint256 _timestamp)
                public
                view
                returns (bytes memory)
            {
                return reports[_queryId].valueByTimestamp[_timestamp];
            }
            /**
             * @dev Used during the upgrade process to verify valid Tellor contracts
             * @return bool value used to verify valid Tellor contracts
             */
            function verify() external pure returns (uint256) {
                return 9999;
            }
            // *****************************************************************************
            // *                                                                           *
            // *                          Internal functions                               *
            // *                                                                           *
            // *****************************************************************************
            /**
             * @dev Updates accumulated staking rewards per staked token
             */
            function _updateRewards() internal {
                if (timeOfLastAllocation == block.timestamp) {
                    return;
                }
                if (totalStakeAmount == 0 || rewardRate == 0) {
                    timeOfLastAllocation = block.timestamp;
                    return;
                }
                // calculate accumulated reward per token staked
                uint256 _newAccumulatedRewardPerShare = accumulatedRewardPerShare +
                    ((block.timestamp - timeOfLastAllocation) * rewardRate * 1e18) /
                    totalStakeAmount;
                // calculate accumulated reward with _newAccumulatedRewardPerShare
                uint256 _accumulatedReward = (_newAccumulatedRewardPerShare *
                    totalStakeAmount) /
                    1e18 -
                    totalRewardDebt;
                if (_accumulatedReward >= stakingRewardsBalance) {
                    // if staking rewards run out, calculate remaining reward per staked
                    // token and set rewardRate to 0
                    uint256 _newPendingRewards = stakingRewardsBalance -
                        ((accumulatedRewardPerShare * totalStakeAmount) /
                            1e18 -
                            totalRewardDebt);
                    accumulatedRewardPerShare +=
                        (_newPendingRewards * 1e18) /
                        totalStakeAmount;
                    rewardRate = 0;
                } else {
                    accumulatedRewardPerShare = _newAccumulatedRewardPerShare;
                }
                timeOfLastAllocation = block.timestamp;
            }
            /**
             * @dev Called whenever a user's stake amount changes. First updates staking rewards,
             * transfers pending rewards to user's address, and finally updates user's stake amount
             * and other relevant variables.
             * @param _stakerAddress address of user whose stake is being updated
             * @param _newStakedBalance new staked balance of user
             */
            function _updateStakeAndPayRewards(
                address _stakerAddress,
                uint256 _newStakedBalance
            ) internal {
                _updateRewards();
                StakeInfo storage _staker = stakerDetails[_stakerAddress];
                if (_staker.stakedBalance > 0) {
                    // if address already has a staked balance, calculate and transfer pending rewards
                    uint256 _pendingReward = (_staker.stakedBalance *
                        accumulatedRewardPerShare) /
                        1e18 -
                        _staker.rewardDebt;
                    // get staker voting participation rate
                    uint256 _numberOfVotes;
                    (bool _success, bytes memory _returnData) = governance.call(
                        abi.encodeWithSignature("getVoteCount()")
                    );
                    if (_success) {
                        _numberOfVotes =
                            uint256(abi.decode(_returnData, (uint256))) -
                            _staker.startVoteCount;
                    }
                    if (_numberOfVotes > 0) {
                        // staking reward = pending reward * voting participation rate
                        (_success, _returnData) = governance.call(
                            abi.encodeWithSignature("getVoteTallyByAddress(address)",_stakerAddress)
                        );
                        if(_success){
                            uint256 _voteTally = abi.decode(_returnData,(uint256));
                            uint256 _tempPendingReward =
                                (_pendingReward *
                                    (_voteTally - _staker.startVoteTally)) /
                                _numberOfVotes;
                            if (_tempPendingReward < _pendingReward) {
                                _pendingReward = _tempPendingReward;
                            }
                        }
                    }
                    stakingRewardsBalance -= _pendingReward;
                    require(token.transfer(msg.sender, _pendingReward));
                    totalRewardDebt -= _staker.rewardDebt;
                    totalStakeAmount -= _staker.stakedBalance;
                }
                _staker.stakedBalance = _newStakedBalance;
                // Update total stakers
                if (_staker.stakedBalance >= stakeAmount) {
                    if (_staker.staked == false) {
                        totalStakers++;
                    }
                    _staker.staked = true;
                } else {
                    if (_staker.staked == true && totalStakers > 0) {
                        totalStakers--;
                    }
                    _staker.staked = false;
                }
                // tracks rewards accumulated before stake amount updated
                _staker.rewardDebt =
                    (_staker.stakedBalance * accumulatedRewardPerShare) /
                    1e18;
                totalRewardDebt += _staker.rewardDebt;
                totalStakeAmount += _staker.stakedBalance;
                // update reward rate if staking rewards are available 
                // given staker's updated parameters
                if(rewardRate == 0) {
                    rewardRate =
                    (stakingRewardsBalance -
                        ((accumulatedRewardPerShare * totalStakeAmount) /
                            1e18 -
                            totalRewardDebt)) /
                    30 days;
                }
            }
            /**
             * @dev Internal function retrieves updated accumulatedRewardPerShare
             * @return uint256 up-to-date accumulated reward per share
             */
            function _getUpdatedAccumulatedRewardPerShare()
                internal
                view
                returns (uint256)
            {
                if (totalStakeAmount == 0) {
                    return accumulatedRewardPerShare;
                }
                uint256 _newAccumulatedRewardPerShare = accumulatedRewardPerShare +
                    ((block.timestamp - timeOfLastAllocation) * rewardRate * 1e18) /
                    totalStakeAmount;
                uint256 _accumulatedReward = (_newAccumulatedRewardPerShare *
                    totalStakeAmount) /
                    1e18 -
                    totalRewardDebt;
                if (_accumulatedReward >= stakingRewardsBalance) {
                    uint256 _newPendingRewards = stakingRewardsBalance -
                        ((accumulatedRewardPerShare * totalStakeAmount) /
                            1e18 -
                            totalRewardDebt);
                    _newAccumulatedRewardPerShare =
                        accumulatedRewardPerShare +
                        (_newPendingRewards * 1e18) /
                        totalStakeAmount;
                }
                return _newAccumulatedRewardPerShare;
            }
        }
        

        File 2 of 3: TellorMaster
        // SPDX-License-Identifier: MIT
        pragma solidity 0.7.4;
        import "./TellorStorage.sol";
        import "./TellorVariables.sol";
        /**
         * @title Tellor Master
         * @dev This is the Master contract with all tellor getter functions and delegate call to Tellor.
         * The logic for the functions on this contract is saved on the TellorGettersLibrary, TellorTransfer,
         * TellorGettersLibrary, and TellorStake
         */
        contract TellorMaster is TellorStorage, TellorVariables {
            event NewTellorAddress(address _newTellor);
            constructor(address _tContract, address _oTellor) {
                addresses[_OWNER] = msg.sender;
                addresses[_DEITY] = msg.sender;
                addresses[_TELLOR_CONTRACT] = _tContract;
                addresses[_OLD_TELLOR] = _oTellor;
                bytesVars[_CURRENT_CHALLENGE] = bytes32("1");
                uints[_DIFFICULTY] = 100;
                uints[_TIME_TARGET] = 240;
                uints[_TARGET_MINERS] = 200;
                uints[_CURRENT_REWARD] = 1e18;
                uints[_DISPUTE_FEE] = 500e18;
                uints[_STAKE_AMOUNT] = 500e18;
                uints[_TIME_OF_LAST_NEW_VALUE] = block.timestamp - 240;
                currentMiners[0].value = 1;
                currentMiners[1].value = 2;
                currentMiners[2].value = 3;
                currentMiners[3].value = 4;
                currentMiners[4].value = 5;
                // Bootstraping Request Queue
                for (uint256 index = 1; index < 51; index++) {
                    Request storage req = requestDetails[index];
                    req.apiUintVars[_REQUEST_Q_POSITION] = index;
                    requestIdByRequestQIndex[index] = index;
                }
                assembly {
                    sstore(_EIP_SLOT, _tContract)
                }
                emit NewTellorAddress(_tContract);
            }
            /**
             * @dev This function allows the Deity to set a new deity
             * @param _newDeity the new Deity in the contract
             */
            function changeDeity(address _newDeity) external {
                require(msg.sender == addresses[_DEITY]);
                addresses[_DEITY] = _newDeity;
            }
            /**
             * @dev This function allows the owner to set a new _owner
             * @param _newOwner the new Owner in the contract
             */
            function changeOwner(address _newOwner) external {
                require(msg.sender == addresses[_OWNER]);
                addresses[_OWNER] = _newOwner;
            }
            /**
             * @dev  allows for the deity to make fast upgrades.  Deity should be 0 address if decentralized
             * @param _tContract the address of the new Tellor Contract
             */
            function changeTellorContract(address _tContract) external {
                require(msg.sender == addresses[_DEITY]);
                addresses[_TELLOR_CONTRACT] = _tContract;
                assembly {
                    sstore(_EIP_SLOT, _tContract)
                }
            }
            /**
             * @dev This is the internal function that allows for delegate calls to the Tellor logic
             * contract address
             */
            function _delegate(address implementation) internal virtual {
                // solhint-disable-next-line no-inline-assembly
                assembly {
                    // Copy msg.data. We take full control of memory in this inline assembly
                    // block because it will not return to Solidity code. We overwrite the
                    // Solidity scratch pad at memory position 0.
                    calldatacopy(0, 0, calldatasize())
                    // Call the implementation.
                    // out and outsize are 0 because we don't know the size yet.
                    let result := delegatecall(
                        gas(),
                        implementation,
                        0,
                        calldatasize(),
                        0,
                        0
                    )
                    // Copy the returned data.
                    returndatacopy(0, 0, returndatasize())
                    switch result
                        // delegatecall returns 0 on error.
                        case 0 {
                            revert(0, returndatasize())
                        }
                        default {
                            return(0, returndatasize())
                        }
                }
            }
            /**
             * @dev This is the fallback function that allows contracts to call the tellor
             * contract at the address stored
             */
            fallback() external payable {
                address addr = addresses[_TELLOR_CONTRACT];
                _delegate(addr);
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.7.4;
        /**
         * @title Tellor Oracle Storage Library
         * @dev Contains all the variables/structs used by Tellor
         */
        contract TellorStorage {
            //Internal struct for use in proof-of-work submission
            struct Details {
                uint256 value;
                address miner;
            }
            struct Dispute {
                bytes32 hash; //unique hash of dispute: keccak256(_miner,_requestId,_timestamp)
                int256 tally; //current tally of votes for - against measure
                bool executed; //is the dispute settled
                bool disputeVotePassed; //did the vote pass?
                bool isPropFork; //true for fork proposal NEW
                address reportedMiner; //miner who submitted the 'bad value' will get disputeFee if dispute vote fails
                address reportingParty; //miner reporting the 'bad value'-pay disputeFee will get reportedMiner's stake if dispute vote passes
                address proposedForkAddress; //new fork address (if fork proposal)
                mapping(bytes32 => uint256) disputeUintVars;
                //Each of the variables below is saved in the mapping disputeUintVars for each disputeID
                //e.g. TellorStorageStruct.DisputeById[disputeID].disputeUintVars[keccak256("requestId")]
                //These are the variables saved in this mapping:
                // uint keccak256("requestId");//apiID of disputed value
                // uint keccak256("timestamp");//timestamp of disputed value
                // uint keccak256("value"); //the value being disputed
                // uint keccak256("minExecutionDate");//7 days from when dispute initialized
                // uint keccak256("numberOfVotes");//the number of parties who have voted on the measure
                // uint keccak256("blockNumber");// the blocknumber for which votes will be calculated from
                // uint keccak256("minerSlot"); //index in dispute array
                // uint keccak256("fee"); //fee paid corresponding to dispute
                mapping(address => bool) voted; //mapping of address to whether or not they voted
            }
            struct StakeInfo {
                uint256 currentStatus; //0-not Staked, 1=Staked, 2=LockedForWithdraw 3= OnDispute 4=ReadyForUnlocking 5=Unlocked
                uint256 startDate; //stake start date
            }
            //Internal struct to allow balances to be queried by blocknumber for voting purposes
            struct Checkpoint {
                uint128 fromBlock; // fromBlock is the block number that the value was generated from
                uint128 value; // value is the amount of tokens at a specific block number
            }
            struct Request {
                uint256[] requestTimestamps; //array of all newValueTimestamps requested
                mapping(bytes32 => uint256) apiUintVars;
                //Each of the variables below is saved in the mapping apiUintVars for each api request
                //e.g. requestDetails[_requestId].apiUintVars[keccak256("totalTip")]
                //These are the variables saved in this mapping:
                // uint keccak256("requestQPosition"); //index in requestQ
                // uint keccak256("totalTip");//bonus portion of payout
                mapping(uint256 => uint256) minedBlockNum; //[apiId][minedTimestamp]=>block.number
                //This the time series of finalValues stored by the contract where uint UNIX timestamp is mapped to value
                mapping(uint256 => uint256) finalValues;
                mapping(uint256 => bool) inDispute; //checks if API id is in dispute or finalized.
                mapping(uint256 => address[5]) minersByValue;
                mapping(uint256 => uint256[5]) valuesByTimestamp;
            }
            uint256[51] requestQ; //uint50 array of the top50 requests by payment amount
            uint256[] public newValueTimestamps; //array of all timestamps requested
            //Address fields in the Tellor contract are saved the addressVars mapping
            //e.g. addressVars[keccak256("tellorContract")] = address
            //These are the variables saved in this mapping:
            // address keccak256("tellorContract");//Tellor address
            // address  keccak256("_owner");//Tellor Owner address
            // address  keccak256("_deity");//Tellor Owner that can do things at will
            // address  keccak256("pending_owner"); // The proposed new owner
            //uint fields in the Tellor contract are saved the uintVars mapping
            //e.g. uintVars[keccak256("decimals")] = uint
            //These are the variables saved in this mapping:
            // keccak256("decimals");    //18 decimal standard ERC20
            // keccak256("disputeFee");//cost to dispute a mined value
            // keccak256("disputeCount");//totalHistoricalDisputes
            // keccak256("total_supply"); //total_supply of the token in circulation
            // keccak256("stakeAmount");//stakeAmount for miners (we can cut gas if we just hardcoded it in...or should it be variable?)
            // keccak256("stakerCount"); //number of parties currently staked
            // keccak256("timeOfLastNewValue"); // time of last challenge solved
            // keccak256("difficulty"); // Difficulty of current block
            // keccak256("currentTotalTips"); //value of highest api/timestamp PayoutPool
            // keccak256("currentRequestId"); //API being mined--updates with the ApiOnQ Id
            // keccak256("requestCount"); // total number of requests through the system
            // keccak256("slotProgress");//Number of miners who have mined this value so far
            // keccak256("miningReward");//Mining Reward in PoWo tokens given to all miners per value
            // keccak256("timeTarget"); //The time between blocks (mined Oracle values)
            // keccak256("_tblock"); //
            // keccak256("runningTips"); // VAriable to track running tips
            // keccak256("currentReward"); // The current reward
            // keccak256("devShare"); // The amount directed towards th devShare
            // keccak256("currentTotalTips"); //
            //This is a boolean that tells you if a given challenge has been completed by a given miner
            mapping(uint256 => uint256) requestIdByTimestamp; //minedTimestamp to apiId
            mapping(uint256 => uint256) requestIdByRequestQIndex; //link from payoutPoolIndex (position in payout pool array) to apiId
            mapping(uint256 => Dispute) public disputesById; //disputeId=> Dispute details
            mapping(bytes32 => uint256) public requestIdByQueryHash; // api bytes32 gets an id = to count of requests array
            mapping(bytes32 => uint256) public disputeIdByDisputeHash; //maps a hash to an ID for each dispute
            mapping(bytes32 => mapping(address => bool)) public minersByChallenge;
            Details[5] public currentMiners; //This struct is for organizing the five mined values to find the median
            mapping(address => StakeInfo) stakerDetails; //mapping from a persons address to their staking info
            mapping(uint256 => Request) requestDetails;
            mapping(bytes32 => uint256) public uints;
            mapping(bytes32 => address) public addresses;
            mapping(bytes32 => bytes32) public bytesVars;
            //ERC20 storage
            mapping(address => Checkpoint[]) public balances;
            mapping(address => mapping(address => uint256)) public _allowances;
            //Migration storage
            mapping(address => bool) public migrated;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.7.4;
        // Helper contract to store hashes of variables
        contract TellorVariables {
            bytes32 constant _BLOCK_NUMBER =
                0x4b4cefd5ced7569ef0d091282b4bca9c52a034c56471a6061afd1bf307a2de7c; //keccak256("_BLOCK_NUMBER");
            bytes32 constant _CURRENT_CHALLENGE =
                0xd54702836c9d21d0727ffacc3e39f57c92b5ae0f50177e593bfb5ec66e3de280; //keccak256("_CURRENT_CHALLENGE");
            bytes32 constant _CURRENT_REQUESTID =
                0xf5126bb0ac211fbeeac2c0e89d4c02ac8cadb2da1cfb27b53c6c1f4587b48020; //keccak256("_CURRENT_REQUESTID");
            bytes32 constant _CURRENT_REWARD =
                0xd415862fd27fb74541e0f6f725b0c0d5b5fa1f22367d9b78ec6f61d97d05d5f8; //keccak256("_CURRENT_REWARD");
            bytes32 constant _CURRENT_TOTAL_TIPS =
                0x09659d32f99e50ac728058418d38174fe83a137c455ff1847e6fb8e15f78f77a; //keccak256("_CURRENT_TOTAL_TIPS");
            bytes32 constant _DEITY =
                0x5fc094d10c65bc33cc842217b2eccca0191ff24148319da094e540a559898961; //keccak256("_DEITY");
            bytes32 constant _DIFFICULTY =
                0xf758978fc1647996a3d9992f611883adc442931dc49488312360acc90601759b; //keccak256("_DIFFICULTY");
            bytes32 constant _DISPUTE_COUNT =
                0x310199159a20c50879ffb440b45802138b5b162ec9426720e9dd3ee8bbcdb9d7; //keccak256("_DISPUTE_COUNT");
            bytes32 constant _DISPUTE_FEE =
                0x675d2171f68d6f5545d54fb9b1fb61a0e6897e6188ca1cd664e7c9530d91ecfc; //keccak256("_DISPUTE_FEE");
            bytes32 constant _DISPUTE_ROUNDS =
                0x6ab2b18aafe78fd59c6a4092015bddd9fcacb8170f72b299074f74d76a91a923; //keccak256("_DISPUTE_ROUNDS");
            bytes32 constant _FEE =
                0x1da95f11543c9b03927178e07951795dfc95c7501a9d1cf00e13414ca33bc409; //keccak256("FEE");
            bytes32 constant _MIN_EXECUTION_DATE =
                0x46f7d53798d31923f6952572c6a19ad2d1a8238d26649c2f3493a6d69e425d28; //keccak256("_MIN_EXECUTION_DATE");
            bytes32 constant _MINER_SLOT =
                0x6de96ee4d33a0617f40a846309c8759048857f51b9d59a12d3c3786d4778883d; //keccak256("_MINER_SLOT");
            bytes32 constant _NUM_OF_VOTES =
                0x1da378694063870452ce03b189f48e04c1aa026348e74e6c86e10738514ad2c4; //keccak256("_NUM_OF_VOTES");
            bytes32 constant _OLD_TELLOR =
                0x56e0987db9eaec01ed9e0af003a0fd5c062371f9d23722eb4a3ebc74f16ea371; //keccak256("_OLD_TELLOR");
            bytes32 constant _ORIGINAL_ID =
                0xed92b4c1e0a9e559a31171d487ecbec963526662038ecfa3a71160bd62fb8733; //keccak256("_ORIGINAL_ID");
            bytes32 constant _OWNER =
                0x7a39905194de50bde334d18b76bbb36dddd11641d4d50b470cb837cf3bae5def; //keccak256("_OWNER");
            bytes32 constant _PAID =
                0x29169706298d2b6df50a532e958b56426de1465348b93650fca42d456eaec5fc; //keccak256("_PAID");
            bytes32 constant _PENDING_OWNER =
                0x7ec081f029b8ac7e2321f6ae8c6a6a517fda8fcbf63cabd63dfffaeaafa56cc0; //keccak256("_PENDING_OWNER");
            bytes32 constant _REQUEST_COUNT =
                0x3f8b5616fa9e7f2ce4a868fde15c58b92e77bc1acd6769bf1567629a3dc4c865; //keccak256("_REQUEST_COUNT");
            bytes32 constant _REQUEST_ID =
                0x9f47a2659c3d32b749ae717d975e7962959890862423c4318cf86e4ec220291f; //keccak256("_REQUEST_ID");
            bytes32 constant _REQUEST_Q_POSITION =
                0xf68d680ab3160f1aa5d9c3a1383c49e3e60bf3c0c031245cbb036f5ce99afaa1; //keccak256("_REQUEST_Q_POSITION");
            bytes32 constant _SLOT_PROGRESS =
                0xdfbec46864bc123768f0d134913175d9577a55bb71b9b2595fda21e21f36b082; //keccak256("_SLOT_PROGRESS");
            bytes32 constant _STAKE_AMOUNT =
                0x5d9fadfc729fd027e395e5157ef1b53ef9fa4a8f053043c5f159307543e7cc97; //keccak256("_STAKE_AMOUNT");
            bytes32 constant _STAKE_COUNT =
                0x10c168823622203e4057b65015ff4d95b4c650b308918e8c92dc32ab5a0a034b; //keccak256("_STAKE_COUNT");
            bytes32 constant _T_BLOCK =
                0xf3b93531fa65b3a18680d9ea49df06d96fbd883c4889dc7db866f8b131602dfb; //keccak256("_T_BLOCK");
            bytes32 constant _TALLY_DATE =
                0xf9e1ae10923bfc79f52e309baf8c7699edb821f91ef5b5bd07be29545917b3a6; //keccak256("_TALLY_DATE");
            bytes32 constant _TARGET_MINERS =
                0x0b8561044b4253c8df1d9ad9f9ce2e0f78e4bd42b2ed8dd2e909e85f750f3bc1; //keccak256("_TARGET_MINERS");
            bytes32 constant _TELLOR_CONTRACT =
                0x0f1293c916694ac6af4daa2f866f0448d0c2ce8847074a7896d397c961914a08; //keccak256("_TELLOR_CONTRACT");
            bytes32 constant _TELLOR_GETTERS =
                0xabd9bea65759494fe86471c8386762f989e1f2e778949e94efa4a9d1c4b3545a; //keccak256("_TELLOR_GETTERS");
            bytes32 constant _TIME_OF_LAST_NEW_VALUE =
                0x2c8b528fbaf48aaf13162a5a0519a7ad5a612da8ff8783465c17e076660a59f1; //keccak256("_TIME_OF_LAST_NEW_VALUE");
            bytes32 constant _TIME_TARGET =
                0xd4f87b8d0f3d3b7e665df74631f6100b2695daa0e30e40eeac02172e15a999e1; //keccak256("_TIME_TARGET");
            bytes32 constant _TIMESTAMP =
                0x2f9328a9c75282bec25bb04befad06926366736e0030c985108445fa728335e5; //keccak256("_TIMESTAMP");
            bytes32 constant _TOTAL_SUPPLY =
                0xe6148e7230ca038d456350e69a91b66968b222bfac9ebfbea6ff0a1fb7380160; //keccak256("_TOTAL_SUPPLY");
            bytes32 constant _TOTAL_TIP =
                0x1590276b7f31dd8e2a06f9a92867333eeb3eddbc91e73b9833e3e55d8e34f77d; //keccak256("_TOTAL_TIP");
            bytes32 constant _VALUE =
                0x9147231ab14efb72c38117f68521ddef8de64f092c18c69dbfb602ffc4de7f47; //keccak256("_VALUE");
            bytes32 constant _EIP_SLOT =
                0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
        }
        

        File 3 of 3: Tellor360
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.3;
        import "./BaseToken.sol";
        import "./NewTransition.sol";
        import "./interfaces/ITellorFlex.sol";
        /**
         @author Tellor Inc.
         @title Tellor360
         @dev This is the controller contract which defines the functionality for
         * changing the oracle contract address, as well as minting and migrating tokens
        */
        contract Tellor360 is BaseToken, NewTransition {
            // Events
            event NewOracleAddress(address _newOracle, uint256 _timestamp);
            event NewProposedOracleAddress(
                address _newProposedOracle,
                uint256 _timestamp
            );
            // Functions
            /**
             * @dev Constructor used to store new flex oracle address
             * @param _flexAddress is the new oracle contract which will replace the
             * tellorX oracle
             */
            constructor(address _flexAddress) {
                require(_flexAddress != address(0), "oracle address must be non-zero");
                addresses[keccak256("_ORACLE_CONTRACT_FOR_INIT")] = _flexAddress;
            }
            /**
             * @dev Use this function to initiate the contract
             */
            function init() external {
                require(uints[keccak256("_INIT")] == 0, "should only happen once");
                uints[keccak256("_INIT")] = 1;
                // retrieve new oracle address from Tellor360 contract address storage
                NewTransition _newController = NewTransition(
                    addresses[_TELLOR_CONTRACT]
                );
                address _flexAddress = _newController.getAddressVars(
                    keccak256("_ORACLE_CONTRACT_FOR_INIT")
                );
                //on switch over, require tellorFlex values are over 12 hours old
                //then when we switch, the governance switch can be instantaneous
                bytes32 _id = 0x83a7f3d48786ac2667503a61e8c415438ed2922eb86a2906e4ee66d9a2ce4992;
                uint256 _firstTimestamp = IOracle(_flexAddress)
                    .getTimestampbyQueryIdandIndex(_id, 0);
                require(
                    block.timestamp - _firstTimestamp >= 12 hours,
                    "contract should be at least 12 hours old"
                );
                addresses[_ORACLE_CONTRACT] = _flexAddress; //used by Liquity+AMPL for this contract's reads
                //init minting uints (timestamps)
                uints[keccak256("_LAST_RELEASE_TIME_TEAM")] = block.timestamp;
                uints[keccak256("_LAST_RELEASE_TIME_DAO")] = block.timestamp - 12 weeks;
                // transfer dispute fees collected during transition period to team
                _doTransfer(
                    addresses[_GOVERNANCE_CONTRACT],
                    addresses[_OWNER],
                    balanceOf(addresses[_GOVERNANCE_CONTRACT])
                );
            }
            /**
             * @dev Mints tokens of the sender from the old contract to the sender
             */
            function migrate() external {
                require(!migrated[msg.sender], "Already migrated");
                _doMint(
                    msg.sender,
                    BaseToken(addresses[_OLD_TELLOR]).balanceOf(msg.sender)
                );
                migrated[msg.sender] = true;
            }
            /**
             * @dev Use this function to withdraw released tokens to the oracle
             */
            function mintToOracle() external {
                require(uints[keccak256("_INIT")] == 1, "tellor360 not initiated");
                // X - 0.02X = 144 daily time based rewards. X = 146.94
                uint256 _releasedAmount = (146.94 ether *
                    (block.timestamp - uints[keccak256("_LAST_RELEASE_TIME_DAO")])) /
                    86400;
                uints[keccak256("_LAST_RELEASE_TIME_DAO")] = block.timestamp;
                uint256 _stakingRewards = (_releasedAmount * 2) / 100;
                _doMint(addresses[_ORACLE_CONTRACT], _releasedAmount - _stakingRewards);
                // Send staking rewards
                _doMint(address(this), _stakingRewards);
                _allowances[address(this)][
                    addresses[_ORACLE_CONTRACT]
                ] = _stakingRewards;
                ITellorFlex(addresses[_ORACLE_CONTRACT]).addStakingRewards(
                    _stakingRewards
                );
            }
            /**
             * @dev Use this function to withdraw released tokens to the team
             */
            function mintToTeam() external {
                require(uints[keccak256("_INIT")] == 1, "tellor360 not initiated");
                uint256 _releasedAmount = (146.94 ether *
                    (block.timestamp - uints[keccak256("_LAST_RELEASE_TIME_TEAM")])) /
                    (86400);
                uints[keccak256("_LAST_RELEASE_TIME_TEAM")] = block.timestamp;
                _doMint(addresses[_OWNER], _releasedAmount);
            }
            /**
             * @dev This function allows team to gain control of any tokens sent directly to this
             * contract (and send them back))
             */
            function transferOutOfContract() external {
                _doTransfer(address(this), addresses[_OWNER], balanceOf(address(this)));
            }
            /**
             * @dev Use this function to update the oracle contract
             */
            function updateOracleAddress() external {
                bytes32 _queryID = keccak256(
                    abi.encode("TellorOracleAddress", abi.encode(bytes("")))
                );
                bytes memory _proposedOracleAddressBytes;
                (, _proposedOracleAddressBytes, ) = IOracle(addresses[_ORACLE_CONTRACT])
                    .getDataBefore(_queryID, block.timestamp - 12 hours);
                address _proposedOracle = abi.decode(
                    _proposedOracleAddressBytes,
                    (address)
                );
                // If the oracle address being reported is the same as the proposed oracle then update the oracle contract
                // only if 7 days have passed since the new oracle address was made official
                // and if 12 hours have passed since query id 1 was first reported on the new oracle contract
                if (_proposedOracle == addresses[keccak256("_PROPOSED_ORACLE")]) {
                    require(
                        block.timestamp >
                            uints[keccak256("_TIME_PROPOSED_UPDATED")] + 7 days,
                        "must wait 7 days after proposing new oracle"
                    );
                    bytes32 _id = 0x83a7f3d48786ac2667503a61e8c415438ed2922eb86a2906e4ee66d9a2ce4992;
                    uint256 _firstTimestamp = IOracle(_proposedOracle)
                        .getTimestampbyQueryIdandIndex(_id, 0);
                    require(
                        block.timestamp - _firstTimestamp >= 12 hours,
                        "contract should be at least 12 hours old"
                    );
                    addresses[_ORACLE_CONTRACT] = _proposedOracle;
                    emit NewOracleAddress(_proposedOracle, block.timestamp);
                }
                // Otherwise if the current reported oracle is not the proposed oracle, then propose it and
                // start the clock on the 7 days before it can be made official
                else {
                    require(_isValid(_proposedOracle), "invalid oracle address");
                    addresses[keccak256("_PROPOSED_ORACLE")] = _proposedOracle;
                    uints[keccak256("_TIME_PROPOSED_UPDATED")] = block.timestamp;
                    emit NewProposedOracleAddress(_proposedOracle, block.timestamp);
                }
            }
            /**
             * @dev Used during the upgrade process to verify valid Tellor Contracts
             */
            function verify() external pure returns (uint256) {
                return 9999;
            }
            /**Internal Functions */
            /**
             * @dev Used during the upgrade process to verify valid Tellor Contracts and ensure
             * they have the right signature
             * @param _contract is the address of the Tellor contract to verify
             * @return bool of whether or not the address is a valid Tellor contract
             */
            function _isValid(address _contract) internal returns (bool) {
                (bool _success, bytes memory _data) = address(_contract).call(
                    abi.encodeWithSelector(0xfc735e99, "") // verify() signature
                );
                require(
                    _success && abi.decode(_data, (uint256)) > 9000, // An arbitrary number to ensure that the contract is valid
                    "New contract is invalid"
                );
                return true;
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.3;
        interface ITellorFlex {
            function addStakingRewards(uint256 _amount) external;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.3;
        import "./oldContracts/contracts/TellorVars.sol";
        import "./oldContracts/contracts/interfaces/IOracle.sol";
        import "./oldContracts/contracts/tellor3/TellorStorage.sol";
        /**
         @author Tellor Inc.
         @title NewTransition
        * @dev The Transition contract links to the Oracle contract and
        * allows parties (like Liquity) to continue to use the master
        * address to access values which use legacy query IDs (request IDs). 
        */
        contract NewTransition is TellorStorage, TellorVars {
            // Functions
            //Getters
            /**
             * @dev Allows Tellor to read data from the addressVars mapping
             * @param _data is the keccak256("_VARIABLE_NAME") of the variable that is being accessed.
             * These are examples of how the variables are saved within other functions:
             * addressVars[keccak256("_OWNER")]
             * addressVars[keccak256("_TELLOR_CONTRACT")]
             * @return address of the requested variable
             */
            function getAddressVars(bytes32 _data) external view returns (address) {
                return addresses[_data];
            }
            /**
             * @dev Returns the latest value for a specific request ID.
             * @param _requestId the requestId to look up
             * @return uint256 the latest value of the request ID
             * @return bool whether or not the value was successfully retrieved
             */
            function getLastNewValueById(uint256 _requestId)
                external
                view
                returns (uint256, bool)
            {
                uint256 _count = getNewValueCountbyRequestId(_requestId);
                if (_count == 0) {
                    return (0, false);
                }
                uint256 _latestTimestamp = getTimestampbyRequestIDandIndex(
                    _requestId,
                    _count - 1
                );
                return (retrieveData(_requestId, _latestTimestamp), true);
            }
            /**
             * @dev Function is solely for the parachute contract
             */
            function getNewCurrentVariables()
                external
                view
                returns (
                    bytes32 _c,
                    uint256[5] memory _r,
                    uint256 _diff,
                    uint256 _tip
                )
            {
                _r = [uint256(1), uint256(1), uint256(1), uint256(1), uint256(1)];
                _diff = 0;
                _tip = 0;
                _c = keccak256(
                    abi.encode(
                        IOracle(addresses[_ORACLE_CONTRACT]).getTimeOfLastNewValue()
                    )
                );
            }
            /**
             * @dev Counts the number of values that have been submitted for the requestId.
             * @param _requestId the requestId to look up
             * @return uint256 count of the number of values received for the requestId
             */
            function getNewValueCountbyRequestId(uint256 _requestId)
                public
                view
                returns (uint256)
            {
                (bytes32 _queryId, ) = _getQueryIdAndDecimals(_requestId);
                IOracle _oracle = IOracle(addresses[_ORACLE_CONTRACT]);
                // try the new oracle first
                try _oracle.getNewValueCountbyQueryId(_queryId) returns (
                    uint256 _valueCount
                ) {
                    if (_valueCount == 0) {
                        return 0;
                    }
                    // if last value is disputed, subtract 1 from the count until a non-disputed value is found
                    uint256 _timestamp = _oracle.getTimestampbyQueryIdandIndex(
                        _queryId,
                        _valueCount - 1
                    );
                    while (
                        _oracle.isInDispute(_queryId, _timestamp) &&
                        _valueCount > 1
                    ) {
                        _valueCount--;
                        _timestamp = _oracle.getTimestampbyQueryIdandIndex(
                            _queryId,
                            _valueCount - 1
                        );
                    }
                    if (
                        _valueCount == 1 &&
                        _oracle.isInDispute(_queryId, _timestamp)
                    ) {
                        return 0;
                    }
                    return _valueCount;
                } catch {
                    return
                        IOracle(addresses[_ORACLE_CONTRACT]).getTimestampCountById(
                            bytes32(_requestId)
                        );
                }
            }
            /**
             * @dev Gets the timestamp for the value based on its index
             * @param _requestId is the requestId to look up
             * @param _index is the value index to look up
             * @return uint256 timestamp
             */
            function getTimestampbyRequestIDandIndex(uint256 _requestId, uint256 _index)
                public
                view
                returns (uint256)
            {
                (bytes32 _queryId, ) = _getQueryIdAndDecimals(_requestId);
                try
                    IOracle(addresses[_ORACLE_CONTRACT]).getTimestampbyQueryIdandIndex(
                        _queryId,
                        _index
                    )
                returns (uint256 _val) {
                    if(_requestId == 1 && _val > block.timestamp - 15 minutes) {
                        ( , , _val) = IOracle(addresses[_ORACLE_CONTRACT]).getDataBefore(_queryId, block.timestamp - 15 minutes);
                    }
                    return _val;
                } catch {
                    return
                        IOracle(addresses[_ORACLE_CONTRACT]).getReportTimestampByIndex(
                            bytes32(_requestId),
                            _index
                        );
                }
            }
            /**
             * @dev Getter for the variables saved under the TellorStorageStruct uints variable
             * @param _data the variable to pull from the mapping. _data = keccak256("_VARIABLE_NAME")
             * where variable_name is the variables/strings used to save the data in the mapping.
             * The variables names in the TellorVariables contract
             * @return uint256 of specified variable
             */
            function getUintVar(bytes32 _data) external view returns (uint256) {
                return uints[_data];
            }
            /**
             * @dev Getter for if the party is migrated
             * @param _addy address of party
             * @return bool if the party is migrated
             */
            function isMigrated(address _addy) external view returns (bool) {
                return migrated[_addy];
            }
            /**
             * @dev Retrieve value from oracle based on timestamp
             * @param _requestId being requested
             * @param _timestamp to retrieve data/value from
             * @return uint256 value for timestamp submitted
             */
            function retrieveData(uint256 _requestId, uint256 _timestamp)
                public
                view
                returns (uint256)
            {
                (bytes32 _queryId, uint256 _decimalsAdjustment) = _getQueryIdAndDecimals(
                    _requestId
                );
                try
                    IOracle(addresses[_ORACLE_CONTRACT]).getValueByTimestamp(
                        bytes32(_requestId),
                        _timestamp
                    )
                returns (bytes memory _val) {
                    return _sliceUint(_val);
                } catch {
                    bytes memory _val;
                    if (_requestId == 1) {
                        (, _val, ) = IOracle(addresses[_ORACLE_CONTRACT])
                            .getDataBefore(_queryId, block.timestamp - 15 minutes);
                    } else {
                         _val = IOracle(addresses[_ORACLE_CONTRACT])
                        .retrieveData(_queryId, _timestamp);
                    }
                    return (_sliceUint(_val) / (10**_decimalsAdjustment));
                }
            }
            // Internal functions
            /**
             * @dev Utilized to help slice a bytes variable into a uint
             * @param _b is the bytes variable to be sliced
             * @return _number of the sliced uint256
             */
            function _sliceUint(bytes memory _b)
                internal
                pure
                returns (uint256 _number)
            {
                for (uint256 _i = 0; _i < _b.length; _i++) {
                    _number = _number * 2**8;
                    _number = _number + uint8(_b[_i]);
                }
            }
            function _getQueryIdAndDecimals(uint256 _requestId) internal pure returns (bytes32, uint256) {
                bytes32 _queryId;
                uint256 _decimalsAdjustment;
                if(_requestId == 1) {
                    _queryId = 0x83a7f3d48786ac2667503a61e8c415438ed2922eb86a2906e4ee66d9a2ce4992; // SpotPrice(eth, usd)
                    _decimalsAdjustment = 12;
                } else if(_requestId == 10) {
                    _queryId = 0x0d12ad49193163bbbeff4e6db8294ced23ff8605359fd666799d4e25a3aa0e3a; // AmpleforthCustomSpotPrice(0x)
                    _decimalsAdjustment = 0;
                } else if(_requestId == 41) {
                    _queryId = 0x612ec1d9cee860bb87deb6370ed0ae43345c9302c085c1dfc4c207cbec2970d7; // AmpleforthUSPCE(0x)
                    _decimalsAdjustment = 0;
                } else {
                    _queryId = bytes32(_requestId);
                    _decimalsAdjustment = 0;
                }
                return(_queryId, _decimalsAdjustment);
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.3;
        import "./oldContracts/contracts/TellorVars.sol";
        import "./oldContracts/contracts/interfaces/IGovernance.sol";
        import "./oldContracts/contracts/tellor3/TellorStorage.sol";
        /**
         @author Tellor Inc.
         @title BaseToken
         @dev Contains the methods related to ERC20 transfers, allowance, and storage
        */
        contract BaseToken is TellorStorage, TellorVars {
            // Events
            event Approval(
                address indexed _owner,
                address indexed _spender,
                uint256 _value
            ); // ERC20 Approval event
            event Transfer(address indexed _from, address indexed _to, uint256 _value); // ERC20 Transfer Event
            // Functions
            /**
             * @dev This function approves a _spender an _amount of tokens to use
             * @param _spender address receiving the allowance
             * @param _amount amount the spender is being approved for
             * @return bool true if spender approved successfully
             */
            function approve(address _spender, uint256 _amount)
                external
                returns (bool)
            {
                require(_spender != address(0), "ERC20: approve to the zero address");
                _allowances[msg.sender][_spender] = _amount;
                emit Approval(msg.sender, _spender, _amount);
                return true;
            }
            /**
             * @notice Allows tellor team to transfer stake of disputed TellorX reporter
             * NOTE: this does not affect TellorFlex stakes, only disputes during 360 transition period
             * @param _from the staker address holding the tokens being transferred
             * @param _to the address of the recipient
             */
            function teamTransferDisputedStake(address _from, address _to) external {
                require(
                    msg.sender == addresses[_OWNER],
                    "only owner can transfer disputed staked"
                );
                require(
                    stakerDetails[_from].currentStatus == 3,
                    "_from address not disputed"
                );
                stakerDetails[_from].currentStatus = 0;
                _doTransfer(_from, _to, uints[_STAKE_AMOUNT]);
            }
            /**
             * @dev Transfers _amount tokens from message sender to _to address
             * @param _to token recipient
             * @param _amount amount of tokens to send
             * @return success whether the transfer was successful
             */
            function transfer(address _to, uint256 _amount)
                external
                returns (bool success)
            {
                _doTransfer(msg.sender, _to, _amount);
                return true;
            }
            /**
             * @notice Send _amount tokens to _to from _from on the condition it
             * is approved by _from
             * @param _from the address holding the tokens being transferred
             * @param _to the address of the recipient
             * @param _amount the amount of tokens to be transferred
             * @return success whether the transfer was successful
             */
            function transferFrom(
                address _from,
                address _to,
                uint256 _amount
            ) external returns (bool success) {
                require(
                    _allowances[_from][msg.sender] >= _amount,
                    "Allowance is wrong"
                );
                _allowances[_from][msg.sender] -= _amount;
                _doTransfer(_from, _to, _amount);
                return true;
            }
            // Getters
            /**
             * @dev Getter function for remaining spender balance
             * @param _user address of party with the balance
             * @param _spender address of spender of said user's balance
             * @return uint256 the remaining allowance of tokens granted to the _spender from the _user
             */
            function allowance(address _user, address _spender)
                external
                view
                returns (uint256)
            {
                return _allowances[_user][_spender];
            }
            /**
             * @dev This function returns whether or not a given user is allowed to trade a given amount
             * and removes the staked amount if they are staked in TellorX and disputed
             * @param _user address of user
             * @param _amount to check if the user can spend
             * @return bool true if they are allowed to spend the amount being checked
             */
            function allowedToTrade(address _user, uint256 _amount)
                public
                view
                returns (bool)
            {
                if (stakerDetails[_user].currentStatus == 3) {
                    // Subtracts the stakeAmount from balance if the _user is staked and disputed in TellorX
                    return (balanceOf(_user) - uints[_STAKE_AMOUNT] >= _amount);
                }
                return (balanceOf(_user) >= _amount); // Else, check if balance is greater than amount they want to spend
            }
            /**
             * @dev Gets the balance of a given address
             * @param _user the address whose balance to look up
             * @return uint256 the balance of the given _user address
             */
            function balanceOf(address _user) public view returns (uint256) {
                return balanceOfAt(_user, block.number);
            }
            /**
             * @dev Gets the historic balance of a given _user address at a specific _blockNumber
             * @param _user the address whose balance to look up
             * @param _blockNumber the block number at which the balance is queried
             * @return uint256 the balance of the _user address at the _blockNumber specified
             */
            function balanceOfAt(address _user, uint256 _blockNumber)
                public
                view
                returns (uint256)
            {
                TellorStorage.Checkpoint[] storage checkpoints = balances[_user];
                if (
                    checkpoints.length == 0 || checkpoints[0].fromBlock > _blockNumber
                ) {
                    return 0;
                } else {
                    if (_blockNumber >= checkpoints[checkpoints.length - 1].fromBlock)
                        return checkpoints[checkpoints.length - 1].value;
                    // Binary search of the value in the array
                    uint256 _min = 0;
                    uint256 _max = checkpoints.length - 2;
                    while (_max > _min) {
                        uint256 _mid = (_max + _min + 1) / 2;
                        if (checkpoints[_mid].fromBlock == _blockNumber) {
                            return checkpoints[_mid].value;
                        } else if (checkpoints[_mid].fromBlock < _blockNumber) {
                            _min = _mid;
                        } else {
                            _max = _mid - 1;
                        }
                    }
                    return checkpoints[_min].value;
                }
            }
            /**
             * @dev Allows users to access the number of decimals
             */
            function decimals() external pure returns (uint8) {
                return 18;
            }
            /**
             * @dev Allows users to access the token's name
             */
            function name() external pure returns (string memory) {
                return "Tellor Tributes";
            }
            /**
             * @dev Allows users to access the token's symbol
             */
            function symbol() external pure returns (string memory) {
                return "TRB";
            }
            /**
             * @dev Getter for the total_supply of tokens
             * @return uint256 total supply
             */
            function totalSupply() external view returns (uint256) {
                return uints[_TOTAL_SUPPLY];
            }
            // Internal functions
            /**
             * @dev Helps mint new TRB
             * @param _to is the address to send minted amount to
             * @param _amount is the amount of TRB to mint and send
             */
            function _doMint(address _to, uint256 _amount) internal {
                // Ensure to address and mint amount are valid
                require(_amount != 0, "Tried to mint non-positive amount");
                require(_to != address(0), "Receiver is 0 address");
                uint128 _previousBalance = uint128(balanceOf(_to));
                uint128 _sizedAmount = uint128(_amount);
                // Update total supply and balance of _to address
                uints[_TOTAL_SUPPLY] += _amount;
                _updateBalanceAtNow(_to, _previousBalance + _sizedAmount);
                emit Transfer(address(0), _to, _amount);
            }
            /**
             * @dev Completes transfers by updating the balances at the current block number
             * and ensuring the amount does not contain tokens locked for tellorX disputes
             * @param _from address to transfer from
             * @param _to address to transfer to
             * @param _amount amount of tokens to transfer
             */
            function _doTransfer(
                address _from,
                address _to,
                uint256 _amount
            ) internal {
                if (_amount == 0) {
                    return;
                }
                require(
                    allowedToTrade(_from, _amount),
                    "Should have sufficient balance to trade"
                );
                // Update balance of _from address
                uint128 _previousBalance = uint128(balanceOf(_from));
                uint128 _sizedAmount = uint128(_amount);
                _updateBalanceAtNow(_from, _previousBalance - _sizedAmount);
                // Update balance of _to address
                _previousBalance = uint128(balanceOf(_to));
                _updateBalanceAtNow(_to, _previousBalance + _sizedAmount);
                emit Transfer(_from, _to, _amount);
            }
            /**
             * @dev Updates balance checkpoint _amount for a given _user address at the current block number
             * @param _user is the address whose balance to update
             * @param _value is the new balance
             */
            function _updateBalanceAtNow(address _user, uint128 _value) internal {
                Checkpoint[] storage checkpoints = balances[_user];
                // Checks if no checkpoints exist, or if checkpoint block is not current block
                if (
                    checkpoints.length == 0 ||
                    checkpoints[checkpoints.length - 1].fromBlock != block.number
                ) {
                    // If yes, push a new checkpoint into the array
                    checkpoints.push(
                        TellorStorage.Checkpoint({
                            fromBlock: uint128(block.number),
                            value: _value
                        })
                    );
                } else {
                    // Else, update old checkpoint
                    TellorStorage.Checkpoint storage oldCheckPoint = checkpoints[
                        checkpoints.length - 1
                    ];
                    oldCheckPoint.value = _value;
                }
            }
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.3;
        import "./tellor3/TellorVariables.sol";
        /**
         @author Tellor Inc.
         @title TellorVariables
         @dev Helper contract to store hashes of variables.
         * For each of the bytes32 constants, the values are equal to
         * keccak256([VARIABLE NAME])
        */
        contract TellorVars is TellorVariables {
            // Storage
            address constant TELLOR_ADDRESS =
                0x88dF592F8eb5D7Bd38bFeF7dEb0fBc02cf3778a0; // Address of main Tellor Contract
            // Hashes for each pertinent contract
            bytes32 constant _GOVERNANCE_CONTRACT =
                0xefa19baa864049f50491093580c5433e97e8d5e41f8db1a61108b4fa44cacd93;
            bytes32 constant _ORACLE_CONTRACT =
                0xfa522e460446113e8fd353d7fa015625a68bc0369712213a42e006346440891e;
            bytes32 constant _TREASURY_CONTRACT =
                0x1436a1a60dca0ebb2be98547e57992a0fa082eb479e7576303cbd384e934f1fa;
            bytes32 constant _SWITCH_TIME =
                0x6c0e91a96227393eb6e42b88e9a99f7c5ebd588098b549c949baf27ac9509d8f;
            bytes32 constant _MINIMUM_DISPUTE_FEE =
                0x7335d16d7e7f6cb9f532376441907fe76aa2ea267285c82892601f4755ed15f0;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity >=0.7.4;
        /**
          @author Tellor Inc.
          @title TellorStorage
          @dev Contains all the variables/structs used by Tellor
        */
        contract TellorStorage {
            //Internal struct for use in proof-of-work submission
            struct Details {
                uint256 value;
                address miner;
            }
            struct Dispute {
                bytes32 hash; //unique hash of dispute: keccak256(_miner,_requestId,_timestamp)
                int256 tally; //current tally of votes for - against measure
                bool executed; //is the dispute settled
                bool disputeVotePassed; //did the vote pass?
                bool isPropFork; //true for fork proposal NEW
                address reportedMiner; //miner who submitted the 'bad value' will get disputeFee if dispute vote fails
                address reportingParty; //miner reporting the 'bad value'-pay disputeFee will get reportedMiner's stake if dispute vote passes
                address proposedForkAddress; //new fork address (if fork proposal)
                mapping(bytes32 => uint256) disputeUintVars;
                mapping(address => bool) voted; //mapping of address to whether or not they voted
            }
            struct StakeInfo {
                uint256 currentStatus; //0-not Staked, 1=Staked, 2=LockedForWithdraw 3= OnDispute 4=ReadyForUnlocking 5=Unlocked
                uint256 startDate; //stake start date
            }
            //Internal struct to allow balances to be queried by blocknumber for voting purposes
            struct Checkpoint {
                uint128 fromBlock; // fromBlock is the block number that the value was generated from
                uint128 value; // value is the amount of tokens at a specific block number
            }
            struct Request {
                uint256[] requestTimestamps; //array of all newValueTimestamps requested
                mapping(bytes32 => uint256) apiUintVars;
                mapping(uint256 => uint256) minedBlockNum; //[apiId][minedTimestamp]=>block.number
                //This the time series of finalValues stored by the contract where uint UNIX timestamp is mapped to value
                mapping(uint256 => uint256) finalValues;
                mapping(uint256 => bool) inDispute; //checks if API id is in dispute or finalized.
                mapping(uint256 => address[5]) minersByValue;
                mapping(uint256 => uint256[5]) valuesByTimestamp;
            }
            uint256[51] requestQ; //uint50 array of the top50 requests by payment amount
            uint256[] public newValueTimestamps; //array of all timestamps requested
            //This is a boolean that tells you if a given challenge has been completed by a given miner
            mapping(uint256 => uint256) requestIdByTimestamp; //minedTimestamp to apiId
            mapping(uint256 => uint256) requestIdByRequestQIndex; //link from payoutPoolIndex (position in payout pool array) to apiId
            mapping(uint256 => Dispute) public disputesById; //disputeId=> Dispute details
            mapping(bytes32 => uint256) public requestIdByQueryHash; // api bytes32 gets an id = to count of requests array
            mapping(bytes32 => uint256) public disputeIdByDisputeHash; //maps a hash to an ID for each dispute
            mapping(bytes32 => mapping(address => bool)) public minersByChallenge;
            Details[5] public currentMiners; //This struct is for organizing the five mined values to find the median
            mapping(address => StakeInfo) stakerDetails; //mapping from a persons address to their staking info
            mapping(uint256 => Request) requestDetails;
            mapping(bytes32 => uint256) public uints;
            mapping(bytes32 => address) public addresses;
            mapping(bytes32 => bytes32) public bytesVars;
            //ERC20 storage
            mapping(address => Checkpoint[]) public balances;
            mapping(address => mapping(address => uint256)) public _allowances;
            //Migration storage
            mapping(address => bool) public migrated;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.3;
        interface IOracle{
            function getReportTimestampByIndex(bytes32 _queryId, uint256 _index) external view returns(uint256);
            function getNewValueCountbyQueryId(bytes32 _queryId) external view returns(uint256);
            function getValueByTimestamp(bytes32 _queryId, uint256 _timestamp) external view returns(bytes memory);
            function getBlockNumberByTimestamp(bytes32 _queryId, uint256 _timestamp) external view returns(uint256);
            function getReporterByTimestamp(bytes32 _queryId, uint256 _timestamp) external view returns(address);
            function getReporterLastTimestamp(address _reporter) external view returns(uint256);
            function reportingLock() external view returns(uint256);
            function removeValue(bytes32 _queryId, uint256 _timestamp) external;
            function getReportsSubmittedByAddress(address _reporter) external view returns(uint256);
            function getTipsByUser(address _user) external view returns(uint256);
            function tipQuery(bytes32 _queryId, uint256 _tip, bytes memory _queryData) external;
            function submitValue(bytes32 _queryId, bytes calldata _value, uint256 _nonce, bytes memory _queryData) external;
            function burnTips() external;
            function verify() external pure returns(uint);
            function changeReportingLock(uint256 _newReportingLock) external;
            function changeTimeBasedReward(uint256 _newTimeBasedReward) external;
            function getTipsById(bytes32 _queryId) external view returns(uint256);
            function getTimestampCountById(bytes32 _queryId) external view returns(uint256);
            function getTimestampIndexByTimestamp(bytes32 _queryId, uint256 _timestamp) external view returns(uint256);
            function getCurrentValue(bytes32 _queryId) external view returns(bytes memory);
            function getTimeOfLastNewValue() external view returns(uint256);
            function getTimestampbyQueryIdandIndex(bytes32 _queryId, uint256 _index) external view returns(uint256);
            function getDataBefore(bytes32 _queryId, uint256 _timestamp) external view returns(bool, bytes memory, uint256);
            function getTokenAddress() external view returns(address);
            function getStakeAmount() external view returns(uint256);
            function isInDispute(bytes32 _queryId, uint256 _timestamp) external view returns(bool);
            function slashReporter(address _reporter, address _recipient) external returns(uint256);
            function retrieveData(bytes32 _queryId, uint256 _timestamp)
                external
                view
                returns (bytes memory);
            function getStakerInfo(address _stakerAddress)
                external
                view
                returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256
                );
            
        }
        // SPDX-License-Identifier: MIT
        pragma solidity >=0.7.4;
        /**
         @author Tellor Inc.
         @title TellorVariables
         @dev Helper contract to store hashes of variables
        */
        contract TellorVariables {
            bytes32 constant _BLOCK_NUMBER =
                0x4b4cefd5ced7569ef0d091282b4bca9c52a034c56471a6061afd1bf307a2de7c; //keccak256("_BLOCK_NUMBER");
            bytes32 constant _CURRENT_CHALLENGE =
                0xd54702836c9d21d0727ffacc3e39f57c92b5ae0f50177e593bfb5ec66e3de280; //keccak256("_CURRENT_CHALLENGE");
            bytes32 constant _CURRENT_REQUESTID =
                0xf5126bb0ac211fbeeac2c0e89d4c02ac8cadb2da1cfb27b53c6c1f4587b48020; //keccak256("_CURRENT_REQUESTID");
            bytes32 constant _CURRENT_REWARD =
                0xd415862fd27fb74541e0f6f725b0c0d5b5fa1f22367d9b78ec6f61d97d05d5f8; //keccak256("_CURRENT_REWARD");
            bytes32 constant _CURRENT_TOTAL_TIPS =
                0x09659d32f99e50ac728058418d38174fe83a137c455ff1847e6fb8e15f78f77a; //keccak256("_CURRENT_TOTAL_TIPS");
            bytes32 constant _DEITY =
                0x5fc094d10c65bc33cc842217b2eccca0191ff24148319da094e540a559898961; //keccak256("_DEITY");
            bytes32 constant _DIFFICULTY =
                0xf758978fc1647996a3d9992f611883adc442931dc49488312360acc90601759b; //keccak256("_DIFFICULTY");
            bytes32 constant _DISPUTE_COUNT =
                0x310199159a20c50879ffb440b45802138b5b162ec9426720e9dd3ee8bbcdb9d7; //keccak256("_DISPUTE_COUNT");
            bytes32 constant _DISPUTE_FEE =
                0x675d2171f68d6f5545d54fb9b1fb61a0e6897e6188ca1cd664e7c9530d91ecfc; //keccak256("_DISPUTE_FEE");
            bytes32 constant _DISPUTE_ROUNDS =
                0x6ab2b18aafe78fd59c6a4092015bddd9fcacb8170f72b299074f74d76a91a923; //keccak256("_DISPUTE_ROUNDS");
            bytes32 constant _EXTENSION =
                0x2b2a1c876f73e67ebc4f1b08d10d54d62d62216382e0f4fd16c29155818207a4; //keccak256("_EXTENSION");
            bytes32 constant _FEE =
                0x1da95f11543c9b03927178e07951795dfc95c7501a9d1cf00e13414ca33bc409; //keccak256("_FEE");
            bytes32 constant _FORK_EXECUTED =
                0xda571dfc0b95cdc4a3835f5982cfdf36f73258bee7cb8eb797b4af8b17329875; //keccak256("_FORK_EXECUTED");
            bytes32 constant _LOCK =
                0xd051321aa26ce60d202f153d0c0e67687e975532ab88ce92d84f18e39895d907;
            bytes32 constant _MIGRATOR =
                0xc6b005d45c4c789dfe9e2895b51df4336782c5ff6bd59a5c5c9513955aa06307; //keccak256("_MIGRATOR");
            bytes32 constant _MIN_EXECUTION_DATE =
                0x46f7d53798d31923f6952572c6a19ad2d1a8238d26649c2f3493a6d69e425d28; //keccak256("_MIN_EXECUTION_DATE");
            bytes32 constant _MINER_SLOT =
                0x6de96ee4d33a0617f40a846309c8759048857f51b9d59a12d3c3786d4778883d; //keccak256("_MINER_SLOT");
            bytes32 constant _NUM_OF_VOTES =
                0x1da378694063870452ce03b189f48e04c1aa026348e74e6c86e10738514ad2c4; //keccak256("_NUM_OF_VOTES");
            bytes32 constant _OLD_TELLOR =
                0x56e0987db9eaec01ed9e0af003a0fd5c062371f9d23722eb4a3ebc74f16ea371; //keccak256("_OLD_TELLOR");
            bytes32 constant _ORIGINAL_ID =
                0xed92b4c1e0a9e559a31171d487ecbec963526662038ecfa3a71160bd62fb8733; //keccak256("_ORIGINAL_ID");
            bytes32 constant _OWNER =
                0x7a39905194de50bde334d18b76bbb36dddd11641d4d50b470cb837cf3bae5def; //keccak256("_OWNER");
            bytes32 constant _PAID =
                0x29169706298d2b6df50a532e958b56426de1465348b93650fca42d456eaec5fc; //keccak256("_PAID");
            bytes32 constant _PENDING_OWNER =
                0x7ec081f029b8ac7e2321f6ae8c6a6a517fda8fcbf63cabd63dfffaeaafa56cc0; //keccak256("_PENDING_OWNER");
            bytes32 constant _REQUEST_COUNT =
                0x3f8b5616fa9e7f2ce4a868fde15c58b92e77bc1acd6769bf1567629a3dc4c865; //keccak256("_REQUEST_COUNT");
            bytes32 constant _REQUEST_ID =
                0x9f47a2659c3d32b749ae717d975e7962959890862423c4318cf86e4ec220291f; //keccak256("_REQUEST_ID");
            bytes32 constant _REQUEST_Q_POSITION =
                0xf68d680ab3160f1aa5d9c3a1383c49e3e60bf3c0c031245cbb036f5ce99afaa1; //keccak256("_REQUEST_Q_POSITION");
            bytes32 constant _SLOT_PROGRESS =
                0xdfbec46864bc123768f0d134913175d9577a55bb71b9b2595fda21e21f36b082; //keccak256("_SLOT_PROGRESS");
            bytes32 constant _STAKE_AMOUNT =
                0x5d9fadfc729fd027e395e5157ef1b53ef9fa4a8f053043c5f159307543e7cc97; //keccak256("_STAKE_AMOUNT");
            bytes32 constant _STAKE_COUNT =
                0x10c168823622203e4057b65015ff4d95b4c650b308918e8c92dc32ab5a0a034b; //keccak256("_STAKE_COUNT");
            bytes32 constant _T_BLOCK =
                0xf3b93531fa65b3a18680d9ea49df06d96fbd883c4889dc7db866f8b131602dfb; //keccak256("_T_BLOCK");
            bytes32 constant _TALLY_DATE =
                0xf9e1ae10923bfc79f52e309baf8c7699edb821f91ef5b5bd07be29545917b3a6; //keccak256("_TALLY_DATE");
            bytes32 constant _TARGET_MINERS =
                0x0b8561044b4253c8df1d9ad9f9ce2e0f78e4bd42b2ed8dd2e909e85f750f3bc1; //keccak256("_TARGET_MINERS");
            bytes32 constant _TELLOR_CONTRACT =
                0x0f1293c916694ac6af4daa2f866f0448d0c2ce8847074a7896d397c961914a08; //keccak256("_TELLOR_CONTRACT");
            bytes32 constant _TELLOR_GETTERS =
                0xabd9bea65759494fe86471c8386762f989e1f2e778949e94efa4a9d1c4b3545a; //keccak256("_TELLOR_GETTERS");
            bytes32 constant _TIME_OF_LAST_NEW_VALUE =
                0x2c8b528fbaf48aaf13162a5a0519a7ad5a612da8ff8783465c17e076660a59f1; //keccak256("_TIME_OF_LAST_NEW_VALUE");
            bytes32 constant _TIME_TARGET =
                0xd4f87b8d0f3d3b7e665df74631f6100b2695daa0e30e40eeac02172e15a999e1; //keccak256("_TIME_TARGET");
            bytes32 constant _TIMESTAMP =
                0x2f9328a9c75282bec25bb04befad06926366736e0030c985108445fa728335e5; //keccak256("_TIMESTAMP");
            bytes32 constant _TOTAL_SUPPLY =
                0xe6148e7230ca038d456350e69a91b66968b222bfac9ebfbea6ff0a1fb7380160; //keccak256("_TOTAL_SUPPLY");
            bytes32 constant _TOTAL_TIP =
                0x1590276b7f31dd8e2a06f9a92867333eeb3eddbc91e73b9833e3e55d8e34f77d; //keccak256("_TOTAL_TIP");
            bytes32 constant _VALUE =
                0x9147231ab14efb72c38117f68521ddef8de64f092c18c69dbfb602ffc4de7f47; //keccak256("_VALUE");
            bytes32 constant _EIP_SLOT =
                0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
        }
        // SPDX-License-Identifier: MIT
        pragma solidity 0.8.3;
        interface IGovernance{
            enum VoteResult {FAILED,PASSED,INVALID}
            function setApprovedFunction(bytes4 _func, bool _val) external;
            function beginDispute(bytes32 _queryId,uint256 _timestamp) external;
            function delegate(address _delegate) external;
            function delegateOfAt(address _user, uint256 _blockNumber) external view returns (address);
            function executeVote(uint256 _disputeId) external;
            function proposeVote(address _contract,bytes4 _function, bytes calldata _data, uint256 _timestamp) external;
            function tallyVotes(uint256 _disputeId) external;
            function updateMinDisputeFee() external;
            function verify() external pure returns(uint);
            function vote(uint256 _disputeId, bool _supports, bool _invalidQuery) external;
            function voteFor(address[] calldata _addys,uint256 _disputeId, bool _supports, bool _invalidQuery) external;
            function getDelegateInfo(address _holder) external view returns(address,uint);
            function isApprovedGovernanceContract(address _contract) external view returns(bool);
            function isFunctionApproved(bytes4 _func) external view returns(bool);
            function getVoteCount() external view returns(uint256);
            function getVoteRounds(bytes32 _hash) external view returns(uint256[] memory);
            function getVoteInfo(uint256 _disputeId) external view returns(bytes32,uint256[8] memory,bool[2] memory,VoteResult,bytes memory,bytes4,address[2] memory);
            function getDisputeInfo(uint256 _disputeId) external view returns(uint256,uint256,bytes memory, address);
            function getOpenDisputesOnId(uint256 _queryId) external view returns(uint256);
            function didVote(uint256 _disputeId, address _voter) external view returns(bool);
            function getVoteTallyByAddress(address _voter) external view returns (uint256);
            //testing
            function testMin(uint256 a, uint256 b) external pure returns (uint256);
        }