false
false
0
The new Blockscout UI is now open source! Learn how to deploy it here
- We're indexing this chain right now. Some of the counts may be inaccurate.

Contract Address Details

0x61e8FC5D6be8DcA10Bc9aDC793FcA8DEadA6B29A

Contract Name
GrailMarket
Creator
0xbcb5fd–b61d33 at 0x29aede–6da783
Balance
0 ETH
Tokens
Fetching tokens...
Transactions
Fetching transactions...
Transfers
Fetching transfers...
Gas Used
Fetching gas used...
Last Balance Update
1925679
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
GrailMarket




Optimization enabled
true
Compiler version
v0.8.20+commit.a1b79de6




Optimization runs
44444444
EVM Version
paris




Verified at
2024-12-14T16:04:21.012581Z

Constructor Arguments

0x000000000000000000000000bcb5fd84cc71cedc15b4651f18d912f816b61d330000000000000000000000002880ab155794e7179c9ee2e38200202908c17b430000000000000000000000000000000000000000000000000000000000000000

Arg [0] (address) : 0xbcb5fd84cc71cedc15b4651f18d912f816b61d33
Arg [1] (address) : 0x2880ab155794e7179c9ee2e38200202908c17b43
Arg [2] (address) : 0x0000000000000000000000000000000000000000

              

src/GrailMarket.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {IGrailMarket} from "./interfaces/IGrailMarket.sol";
import {Currency} from "./types/Currency.sol";
import {MarketId} from "./types/MarketId.sol";
import {Market} from "./libraries/Market.sol";
import {BaseMarket} from "./base/BaseMarket.sol";
import {BaseMarketConfig} from "./base/BaseMarketConfig.sol";

contract GrailMarket is IGrailMarket, BaseMarket, BaseMarketConfig {
    constructor(address _owner, address _pyth, Currency _currency) BaseMarketConfig(_owner, _pyth, _currency) {}

    /// @inheritdoc IGrailMarket
    function resolve(MarketId id, uint256 roundId, bytes calldata priceUpdate) external payable override {
        Market.MarketInfo storage market = markets[id];
        Market.Round storage round = market.rounds[roundId];

        // ensure round status can be setlled
        if (round.status != Market.RoundStatus.LIVE) revert InvalidRoundStatus();

        // ensure the timing is right
        if (round.closingTime > block.timestamp) revert ActionTooEarly();

        (int64 closingPrice,) =
            _fetchAssetPrice(market.oracleId, priceUpdate, round.closingTime, round.closingTime + MAX_SECONDS_OFFSET);

        // lock the following n + 1 round
        _lockMarketRound(market, id, roundId + 1, closingPrice);
        // start another n + 2 round
        _startNextRound(market, id);

        // only collect protocol fee when position exists on both sides
        bool isRefunding;
        uint256 winningSide;

        if (round.bearShares > 0 && round.bullShares > 0) {
            // calculate protocol fee
            uint128 protocolFee = (round.totalShares * config.protocolFeeBps) / BASIS_POINT;
            // calculate incentive fee
            uint128 incentiveFee = (protocolFee * config.incentiveFeeBps) / BASIS_POINT;
            uint128 netProtocolFee = protocolFee - incentiveFee;
            round.rewardPool = round.totalShares - protocolFee;

            // account for fee
            unchecked {
                protocolFeesAccrued += netProtocolFee;
            }

            // if closing price greater than locked price, market was bullish, the bulls wins
            if (closingPrice > round.priceMark) {
                round.winningShares = round.bullShares;
                winningSide = 1;
                // if closing price less than locked price, market was bearish, the bears wins
            } else if (closingPrice < round.priceMark) {
                round.winningShares = round.bearShares;
            }

            round.status = Market.RoundStatus.RESOLVED;
            round.closingPrice = closingPrice;
            // transfer the resolver fee
            _tryCredit(msg.sender, incentiveFee);
        } else {
            // we should probably refund as we are not interested in the protocol winning
            isRefunding = true;
            round.status = Market.RoundStatus.REFUNDING;
            round.closingPrice = closingPrice;
        }

        emit Resolve(id, roundId, round.rewardPool, round.winningShares, closingPrice, winningSide, isRefunding);
    }

    /// @inheritdoc IGrailMarket
    function setResolverFee(uint16 fee) external override onlyOwner {
        // ensure fee is moderate
        if (fee > MAX_PROTOCOL_FEE) revert FeeTooLarge(fee);

        config.incentiveFeeBps = fee;
        emit SetProtocolFee(fee);
    }
}
        

lib/solady/src/auth/Ownable.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}
          

node_modules/@pythnetwork/pyth-sdk-solidity/IPyth.sol

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "./PythStructs.sol";
import "./IPythEvents.sol";

/// @title Consume prices from the Pyth Network (https://pyth.network/).
/// @dev Please refer to the guidance at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how to consume prices safely.
/// @author Pyth Data Association
interface IPyth is IPythEvents {
    /// @notice Returns the price of a price feed without any sanity checks.
    /// @dev This function returns the most recent price update in this contract without any recency checks.
    /// This function is unsafe as the returned price update may be arbitrarily far in the past.
    ///
    /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
    /// sufficiently recent for their application. If you are considering using this function, it may be
    /// safer / easier to use `getPriceNoOlderThan`.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getPriceUnsafe(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the price that is no older than `age` seconds of the current time.
    /// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
    /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
    /// recently.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getPriceNoOlderThan(
        bytes32 id,
        uint age
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
    /// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
    /// However, if the price is not recent this function returns the latest available price.
    ///
    /// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
    /// the returned price is recent or useful for any particular application.
    ///
    /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
    /// sufficiently recent for their application. If you are considering using this function, it may be
    /// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getEmaPriceUnsafe(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
    /// of the current time.
    /// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
    /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
    /// recently.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getEmaPriceNoOlderThan(
        bytes32 id,
        uint age
    ) external view returns (PythStructs.Price memory price);

    /// @notice Update price feeds with given update messages.
    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
    /// `getUpdateFee` with the length of the `updateData` array.
    /// Prices will be updated if they are more recent than the current stored prices.
    /// The call will succeed even if the update is not the most recent.
    /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
    /// @param updateData Array of price update data.
    function updatePriceFeeds(bytes[] calldata updateData) external payable;

    /// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is
    /// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the
    /// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`.
    ///
    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
    /// `getUpdateFee` with the length of the `updateData` array.
    ///
    /// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime
    /// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have
    /// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
    /// Otherwise, it calls updatePriceFeeds method to update the prices.
    ///
    /// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid.
    /// @param updateData Array of price update data.
    /// @param priceIds Array of price ids.
    /// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]`
    function updatePriceFeedsIfNecessary(
        bytes[] calldata updateData,
        bytes32[] calldata priceIds,
        uint64[] calldata publishTimes
    ) external payable;

    /// @notice Returns the required fee to update an array of price updates.
    /// @param updateData Array of price update data.
    /// @return feeAmount The required fee in Wei.
    function getUpdateFee(
        bytes[] calldata updateData
    ) external view returns (uint feeAmount);

    /// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published
    /// within `minPublishTime` and `maxPublishTime`.
    ///
    /// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
    /// otherwise, please consider using `updatePriceFeeds`. This method may store the price updates on-chain, if they
    /// are more recent than the current stored prices.
    ///
    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
    /// `getUpdateFee` with the length of the `updateData` array.
    ///
    ///
    /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
    /// no update for any of the given `priceIds` within the given time range.
    /// @param updateData Array of price update data.
    /// @param priceIds Array of price ids.
    /// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
    /// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
    /// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
    function parsePriceFeedUpdates(
        bytes[] calldata updateData,
        bytes32[] calldata priceIds,
        uint64 minPublishTime,
        uint64 maxPublishTime
    ) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);

    /// @notice Similar to `parsePriceFeedUpdates` but ensures the updates returned are
    /// the first updates published in minPublishTime. That is, if there are multiple updates for a given timestamp,
    /// this method will return the first update. This method may store the price updates on-chain, if they
    /// are more recent than the current stored prices.
    ///
    ///
    /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
    /// no update for any of the given `priceIds` within the given time range and uniqueness condition.
    /// @param updateData Array of price update data.
    /// @param priceIds Array of price ids.
    /// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
    /// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
    /// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
    function parsePriceFeedUpdatesUnique(
        bytes[] calldata updateData,
        bytes32[] calldata priceIds,
        uint64 minPublishTime,
        uint64 maxPublishTime
    ) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
}
          

node_modules/@pythnetwork/pyth-sdk-solidity/IPythEvents.sol

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @title IPythEvents contains the events that Pyth contract emits.
/// @dev This interface can be used for listening to the updates for off-chain and testing purposes.
interface IPythEvents {
    /// @dev Emitted when the price feed with `id` has received a fresh update.
    /// @param id The Pyth Price Feed ID.
    /// @param publishTime Publish time of the given price update.
    /// @param price Price of the given price update.
    /// @param conf Confidence interval of the given price update.
    event PriceFeedUpdate(
        bytes32 indexed id,
        uint64 publishTime,
        int64 price,
        uint64 conf
    );
}
          

node_modules/@pythnetwork/pyth-sdk-solidity/PythStructs.sol

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

contract PythStructs {
    // A price with a degree of uncertainty, represented as a price +- a confidence interval.
    //
    // The confidence interval roughly corresponds to the standard error of a normal distribution.
    // Both the price and confidence are stored in a fixed-point numeric representation,
    // `x * (10^expo)`, where `expo` is the exponent.
    //
    // Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how
    // to how this price safely.
    struct Price {
        // Price
        int64 price;
        // Confidence interval around the price
        uint64 conf;
        // Price exponent
        int32 expo;
        // Unix timestamp describing when the price was published
        uint publishTime;
    }

    // PriceFeed represents a current aggregate price from pyth publisher feeds.
    struct PriceFeed {
        // The price ID.
        bytes32 id;
        // Latest available price
        Price price;
        // Latest available exponentially-weighted moving average price
        Price emaPrice;
    }
}
          

src/base/BaseMarket.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {IBaseMarket} from "../interfaces/IBaseMarket.sol";
import {ICommonEvents} from "../interfaces/ICommonEvents.sol";
import {MarketId} from "../types/MarketId.sol";
import {Currency} from "../types/Currency.sol";

import {Option, OptionLibrary} from "../libraries/Option.sol";
import {Position} from "../libraries/Position.sol";
import {Market} from "../libraries/Market.sol";

import {Ownable} from "solady/auth/Ownable.sol";

abstract contract BaseMarket is IBaseMarket, ICommonEvents, Ownable {
    using OptionLibrary for Option;

    Currency public immutable currency;
    // @dev keeps track of total value locked to aid token recovery
    uint256 public tvl;

    /// @dev keeps track of each markets data
    mapping(MarketId id => Market.MarketInfo) public markets;

    constructor(Currency _currency) {
        currency = _currency;
    }

    /// @inheritdoc IBaseMarket
    function bullish(MarketId id, uint256 roundId, uint256 stake) external payable override {
        bytes32 positionId = _takePosition(id, roundId, msg.sender, uint128(stake), Option.wrap(1));
        // try debiting the user
        _tryDebit(msg.sender, stake, msg.value);
        emit Bullish(id, roundId, msg.sender, positionId, stake);
    }

    /// @inheritdoc IBaseMarket
    function bearish(MarketId id, uint256 roundId, uint256 stake) external payable override {
        bytes32 positionId = _takePosition(id, roundId, msg.sender, uint128(stake), Option.wrap(0));
        // try debiting the user
        _tryDebit(msg.sender, stake, msg.value);
        emit Bearish(id, roundId, msg.sender, positionId, stake);
    }

    /// @inheritdoc IBaseMarket
    function settle(MarketId id, uint256 roundId) external override {
        uint256 reward = _settle(id, roundId, msg.sender);
        // credit the user
        _tryCredit(msg.sender, reward);
    }

    /// @inheritdoc IBaseMarket
    function settleFor(MarketId id, uint256 roundId, address account) external override {
        uint256 reward = _settle(id, roundId, account);
        // credit the user
        _tryCredit(account, reward);
    }

    /// @inheritdoc IBaseMarket
    function settleBatch(MarketId id, uint256[] calldata roundIds) external override {
        uint256 len = roundIds.length;
        uint256 reward;

        for (uint256 i = 0; i < len;) {
            reward = _settle(id, roundIds[i], msg.sender);

            unchecked {
                ++i;
            }
        }

        // credit the user
        _tryCredit(msg.sender, reward);
    }

    /// @inheritdoc IBaseMarket
    function claimRefund(MarketId id, uint256 roundId) external override {
        Market.MarketInfo storage market = markets[id];
        Market.Round storage round = market.rounds[roundId];

        bytes32 positionId = Position.toId(id, roundId, msg.sender);
        Position.State storage position = market.positions[positionId];

        // ensure the round is refunding
        if (round.status != Market.RoundStatus.REFUNDING) revert InvalidRoundStatus();
        // ensure the user has not claimed before
        if (position.settled) revert RewardClaimed();

        position.settled = true;

        // credit user stake
        _tryCredit(msg.sender, position.stake);
        emit ClaimRefund(id, roundId, msg.sender, positionId, position.stake);
    }

    /// @inheritdoc IBaseMarket
    function recoverToken(Currency token, address recipient, uint256 amount) external override onlyOwner {
        token.transfer(recipient, amount);

        /// @notice ensure recovery will not have negative impact on the total value locked
        uint256 balanceAfter = token.balanceOfSelf();
        if (token == currency && balanceAfter < tvl) revert InsufficientBalance();

        emit RecoverToken(token, recipient, amount);
    }

    /// @inheritdoc IBaseMarket
    function getRoundInfo(MarketId id, uint256 roundId, address account)
        external
        view
        override
        returns (Market.Round memory r, Position.State memory p)
    {
        Market.MarketInfo storage market = markets[id];
        r = market.rounds[roundId];

        bytes32 positionId = Position.toId(id, roundId, account);
        p = market.positions[positionId];
    }

    /// @inheritdoc IBaseMarket
    function getPositionInfo(MarketId id, uint256 roundId, address account)
        external
        view
        returns (Position.State memory r)
    {
        Market.MarketInfo storage market = markets[id];
        bytes32 positionId = Position.toId(id, roundId, account);
        r = market.positions[positionId];
    }

    /**
     * ============================== Private/Internal functions =================================
     */
    function _takePosition(MarketId id, uint256 roundId, address account, uint128 stake, Option option)
        private
        returns (bytes32 positionId)
    {
        Market.MarketInfo storage market = markets[id];
        Market.Round storage round = market.rounds[roundId];

        // ensure market is initialized
        if (market.oracleId == bytes32(0)) revert MarketDoesNotExist();
        // ensure round entry is open
        if (round.status != Market.RoundStatus.OPEN) revert EntryNotAllowed();
        // ensure stake is not zero
        if (stake == 0) revert CannotStakeZeroAmount();

        positionId = Position.toId(id, roundId, account);
        Position.State storage position = market.positions[positionId];

        // ensure user has no previous position
        if (position.stake > 0) revert PositionAlreadyExist();

        // if all checks well, open position
        // probably not going to overflow, even if Elon Musk stakes all his wealth, LOL!
        unchecked {
            round.totalShares += stake;

            if (option.isBullish()) {
                round.bullShares += stake;
            } else {
                round.bearShares += stake;
            }
        }

        position.stake = stake;
        position.option = option;
    }

    function _settle(MarketId id, uint256 roundId, address account) private returns (uint256 reward) {
        Market.MarketInfo storage market = markets[id];
        Market.Round storage round = market.rounds[roundId];

        bytes32 positionId = Position.toId(id, roundId, account);
        Position.State storage position = market.positions[positionId];

        // ensure round has been settled
        if (round.status != Market.RoundStatus.RESOLVED) revert InvalidRoundStatus();
        // ensure user has a valid position
        if (position.stake == 0) revert PositionNotFound();
        // ensure user has not claimed reward already
        if (position.settled) revert RewardClaimed();

        // tag claimed to avoid reentrancy
        position.settled = true;
        bool isRewardable;

        if (position.option.isBullish()) {
            isRewardable = round.closingPrice > round.priceMark;
        } else {
            isRewardable = round.closingPrice < round.priceMark;
        }

        if (isRewardable) {
            reward = ((position.stake * round.rewardPool) / round.winningShares);
        } else {
            revert SorryNoReward();
        }

        emit Settle(id, roundId, account, positionId, reward);
    }

    /**
     * @dev allow transfer or crediting user balance
     *
     * @param account the address of user debit from
     * @param amount the amount to debit
     */
    function _tryCredit(address account, uint256 amount) internal {
        unchecked {
            tvl -= amount;
        }
        currency.transfer(account, amount);
    }

    /**
     * @dev allow pulling or debiting user balance
     *
     * @param account the address of user debit from
     * @param amount the amount to debit
     * @param value the original message value
     */
    function _tryDebit(address account, uint256 amount, uint256 value) internal {
        unchecked {
            tvl += amount;
        }

        if (currency.isNative()) {
            if (amount != value) revert InsufficientFee();
        } else {
            currency.safeTransferFrom(account, address(this), amount);
        }
    }
}
          

src/base/BaseMarketConfig.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {IBaseMarketConfig} from "../interfaces/IBaseMarketConfig.sol";
import {IPyth} from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import {Currency} from "../types/Currency.sol";
import {MarketId} from "../types/MarketId.sol";

import {Market} from "../libraries/Market.sol";
import {PythStructs} from "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";

import {BaseMarket} from "./BaseMarket.sol";

abstract contract BaseMarketConfig is IBaseMarketConfig, BaseMarket {
    uint16 public constant MAX_PROTOCOL_FEE = 1000; // 10%
    uint16 public constant BASIS_POINT = 10_000;
    uint8 public constant MAX_SECONDS_OFFSET = 10;
    uint256 public constant CAN_CANCEL_AFTER = 15 minutes;

    IPyth public immutable pyth;

    uint256 public protocolFeesAccrued;
    Market.Config public config;

    constructor(address _owner, address _pyth, Currency _currency) BaseMarket(_currency) {
        _initializeOwner(_owner);

        pyth = IPyth(_pyth);

        config.duration = uint48(5 minutes);
        config.incentiveFeeBps = 500; // 5%
        config.protocolFeeBps = 500; // 5%
    }

    /// @inheritdoc IBaseMarketConfig
    function createMarket(MarketId id, bytes calldata priceUpdate) external payable override {
        Market.MarketInfo storage market = markets[id];
        bytes32 oracleId = MarketId.unwrap(id);

        // ensure market does not exist already
        if (market.oracleId != bytes32(0)) revert MarketAlreadyExist();

        uint64 minPublishTime = uint64(block.timestamp - 5 minutes);
        // no use for the price, just to ascertain a priceFeed exists on-chain
        (int64 price,) = _fetchAssetPrice(oracleId, priceUpdate, minPublishTime, uint64(block.timestamp));

        market.oracleId = MarketId.unwrap(id);

        emit CreateMarket(id, msg.sender);

        // start and lock the genesis round
        _startNextRound(market, id);
        // lock the genesis round
        _lockMarketRound(market, id, 1, price);
        // start n + 2 round
        _startNextRound(market, id);
    }

    /// @inheritdoc IBaseMarketConfig
    function collectProtocolFee(address recipient, uint256 amount) external override onlyOwner {
        uint256 amountCollected;

        amountCollected = (amount == 0) ? protocolFeesAccrued : amount;
        protocolFeesAccrued -= amountCollected;

        // credit the recipient
        _tryCredit(recipient, amountCollected);
        emit CollectFee(currency, msg.sender, recipient, amountCollected);
    }

    /// @inheritdoc IBaseMarketConfig
    function setProtocolFee(uint16 fee) external override onlyOwner {
        // ensure fee is moderate
        if (fee > MAX_PROTOCOL_FEE) revert FeeTooLarge(fee);

        config.protocolFeeBps = fee;
        emit SetProtocolFee(fee);
    }

    /// @inheritdoc IBaseMarketConfig
    function setMarketDuration(uint32 duration) external override onlyOwner {
        config.duration = duration;
        emit SetMarketDuration(duration);
    }

    /// @inheritdoc IBaseMarketConfig
    function setMinStakeAmount(uint256 minStakeAmount) external override onlyOwner {
        config.minStakeAmount = uint176(minStakeAmount);
        emit SetMinStakeAmount(minStakeAmount);
    }

    /// @inheritdoc IBaseMarketConfig
    function cancelRound(MarketId id, uint256 roundId, bytes calldata priceUpdate) external payable override {
        Market.MarketInfo storage market = markets[id];
        Market.Round storage round = market.rounds[roundId];

        // ensure the round status allows cancellation
        if (round.status != Market.RoundStatus.LIVE) revert InvalidRoundStatus();

        // ensure users can only cancel when round cannot be locked/settled
        if (round.closingTime + CAN_CANCEL_AFTER > block.timestamp) revert ActionTooEarly();

        round.status = Market.RoundStatus.REFUNDING;

        // fetch asset price
        (int64 price,) =
            _fetchAssetPrice(market.oracleId, priceUpdate, uint64(block.timestamp - 1 minutes), uint64(block.timestamp));

        /// @dev we should also maintain the loop by
        // locking n + 1 round
        _lockMarketRound(market, id, roundId + 1, price);
        // start n + 2 round
        _startNextRound(market, id);

        emit CancelRound(id, roundId);
    }

    /**
     * ============================ Internal functions ==================================
     */
    function _fetchAssetPrice(
        bytes32 priceFeedId,
        bytes calldata priceUpdateData,
        uint64 minPublishTime,
        uint64 maxPublishTime
    ) internal returns (int64 latestPrice, uint256 publishTime) {
        bytes32[] memory priceFeedIds = new bytes32[](1);
        priceFeedIds[0] = priceFeedId;

        bytes[] memory updateData = new bytes[](1);
        updateData[0] = priceUpdateData;

        uint256 updateFee = pyth.getUpdateFee(updateData);
        if (msg.value < updateFee) revert InsufficientFee();

        PythStructs.PriceFeed[] memory priceFeeds =
            pyth.parsePriceFeedUpdates{value: updateFee}(updateData, priceFeedIds, minPublishTime, maxPublishTime);

        PythStructs.PriceFeed memory priceFeed = priceFeeds[0];
        PythStructs.Price memory price = priceFeed.price;

        latestPrice = price.price;
        publishTime = price.publishTime;
    }

    function _startNextRound(Market.MarketInfo storage market, MarketId id) internal {
        uint256 roundId = ++market.roundId;

        uint64 openingTime = uint64(block.timestamp);
        uint64 closingTime = uint64(openingTime + (config.duration * 2));

        Market.Round storage round = market.rounds[roundId];
        round.status = Market.RoundStatus.OPEN;
        round.openingTime = openingTime;
        round.closingTime = closingTime;

        emit NewRound(id, roundId, openingTime, closingTime);
    }

    function _lockMarketRound(Market.MarketInfo storage market, MarketId id, uint256 roundId, int64 price) internal {
        Market.Round storage round = market.rounds[roundId];
        uint64 closingTime = uint64(block.timestamp + config.duration);
        round.status = Market.RoundStatus.LIVE;
        round.closingTime = closingTime;
        round.priceMark = price;

        emit SetRoundPriceMark(id, roundId, price, closingTime);
    }
}
          

src/interfaces/IBaseMarket.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {Currency} from "../types/Currency.sol";
import {MarketId} from "../types/MarketId.sol";
import {Market} from "../libraries/Market.sol";
import {Position} from "../libraries/Position.sol";

interface IBaseMarket {
    /// @notice Revert when trying to spend more than your available balance
    error InsufficientBalance();

    /// @notice Revert when caller provides insufficient fee for Pyth calls
    error InsufficientFee();

    /// @notice Revert when it is too early to perform an action
    error ActionTooEarly();

    /// @notice Revert if market already exist
    error MarketAlreadyExist();

    /// @notice Revert if round status does not meet action requirement
    error InvalidRoundStatus();

    /// @notice Revert if reward has been claimed
    error RewardClaimed();

    /// @notice Revert if no reward to claim
    error SorryNoReward();

    /// @notice Revert when trying to interact with non-existing market
    error MarketDoesNotExist();

    /// @notice Revert when a round entry is no longer allowed
    error EntryNotAllowed();

    /// @notice Revert when trying to open a position double position
    error PositionAlreadyExist();

    /// @notice Revert if the position has no stake
    error PositionNotFound();

    /// @notice Revert when stake is zero
    error CannotStakeZeroAmount();

    /// @notice Revert when trying to recover accepted token in cross-chain pools
    error CannotRecoverCurrency();

    /**
     * @notice Called to place a bullish bet on a market
     *
     * @param id the identifier of market to open position in
     * @param roundId the unique id of the active round
     * @param stake the amount staked as wager for position
     */
    function bullish(MarketId id, uint256 roundId, uint256 stake) external payable;

    /**
     * @notice Called to place a bearish bet on a market
     *
     * @param id the identifier of market to open position in
     * @param roundId the unique id of the active round
     * @param stake the amount staked as wager for position
     */
    function bearish(MarketId id, uint256 roundId, uint256 stake) external payable;

    /**
     * @dev Allows owner to settle a position, more like claiming rewards accrued in `roundId`
     *
     * @param id market identifier
     * @param roundId round identifier
     */
    function settle(MarketId id, uint256 roundId) external;

    /**
     * @dev Allows anyone to settle a position, more like claiming rewards accrued in `roundId`
     * @notice Anyone can claim onbehalf of someone else, opening door for automating claiming to improve UX
     *
     * @param id market identifier
     * @param roundId round identifier
     * @param account the position owner
     */
    function settleFor(MarketId id, uint256 roundId, address account) external;

    /**
     * @dev Allows anyone to settle positions, more like claiming rewards accrued in `roundIds`
     * @notice Only the position owner can claim batch reward
     *
     * @param id market identifier
     * @param roundIds array of round identifiers
     */
    function settleBatch(MarketId id, uint256[] calldata roundIds) external;

    /**
     * @dev Allows anyone to claim refund of cancelled round
     * @notice Can be called by onyone on behalf of someone else
     *
     * @param id market identifier
     * @param roundId round identifier
     */
    function claimRefund(MarketId id, uint256 roundId) external;

    /**
     * @notice useful for recovering native/local tokens sent to the contract by mistake
     *
     * @param currency address of token to withdraw
     * @param recipient address of token receiver
     * @param amount amount of token to withdraw
     */
    function recoverToken(Currency currency, address recipient, uint256 amount) external;

    /**
     * @dev Returns market round info
     *
     * @param id market identifier
     * @param roundId round identifier
     * @param account address to fetch position for
     *
     */
    function getRoundInfo(MarketId id, uint256 roundId, address account)
        external
        view
        returns (Market.Round memory r, Position.State memory p);

    /**
     * @dev Returns market round info
     *
     * @param id market identifier
     * @param roundId round identifier
     * @return market market info
     *
     */
    function getPositionInfo(MarketId id, uint256 roundId, address account)
        external
        view
        returns (Position.State memory);
}
          

src/interfaces/IBaseMarketConfig.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {Currency} from "../types/Currency.sol";
import {MarketId} from "../types/MarketId.sol";

interface IBaseMarketConfig {
    /// @notice Revert when protocol fee exceeds maximum allowed
    error FeeTooLarge(uint16 fee);

    /**
     * @dev Emitted when the protocol manager collects accrued fees
     */
    event CollectFee(Currency indexed currency, address indexed collector, address indexed recipient, uint256 amount);

    /**
     * @dev Emitted when a new protocol fee is set for market
     */
    event SetProtocolFee(uint16 newFee);

    /**
     * @dev Emitted when a new resolver fee is set
     */
    event SetResolverFee(uint16 newFee);

    /**
     * @dev Emitted when the market duration is updated
     */
    event SetMarketDuration(uint32 duration);

    /**
     * @notice Emitted when the minimum stake for a market is updated
     */
    event SetMinStakeAmount(uint256 minStakeAmount);

    /**
     * @dev Allows anyone to initialize a new market if it's not yet existing
     *
     * @param id Pyth oracle price feed identifier
     * @param priceUpdate pyth price update data
     */
    function createMarket(MarketId id, bytes calldata priceUpdate) external payable;

    /**
     * @dev Allows protocol managers to collect accrued fee
     *
     * @param recipient address to forward fund to
     * @param amount amount
     */
    function collectProtocolFee(address recipient, uint256 amount) external;

    /**
     * @dev Allows protocol managers to set fee for market
     *
     * @param fee the fee in BPS
     */
    function setProtocolFee(uint16 fee) external;

    /**
     * @dev Allow managers to adjust how long a round lasts
     *
     * @param duration the new duration in seconds
     */
    function setMarketDuration(uint32 duration) external;

    /**
     * @dev Allows protocol managers to set minimum stake amount
     * for market
     *
     * @param minStakeAmount the minimum allowed stake
     */
    function setMinStakeAmount(uint256 minStakeAmount) external;

    /**
     * @dev Allows anyone to cancel round to allow users claim refund of stakes
     * @notice Calling this function will cancel round with `roundId`, locks round
     * with id roundId + 1 and equally starts a new round with id of roundId + 2
     *
     * @param id market identifier
     * @param roundId id of round to cancel
     * @param priceUpdate pyth price update data
     */
    function cancelRound(MarketId id, uint256 roundId, bytes calldata priceUpdate) external payable;
}
          

src/interfaces/ICommonEvents.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {Currency} from "../types/Currency.sol";
import {MarketId} from "../types/MarketId.sol";

interface ICommonEvents {
    /**
     * @dev Emitted when a new prediction round is started
     */
    event NewRound(MarketId indexed id, uint256 indexed roundId, uint256 openingTime, uint256 closingTime);

    /**
     * @dev Emitted when a round's price mark is locked
     */
    event SetRoundPriceMark(MarketId indexed id, uint256 indexed roundId, int256 priceMark, uint256 closingTime);

    /**
     * @dev Emitted when a round is resolved
     */
    event Resolve(
        MarketId indexed id,
        uint256 indexed roundId,
        uint256 rewardPool,
        uint256 totalWinningStake,
        int256 closingPrice,
        uint256 winningSide,
        bool isRefunding
    );

    /**
     * @dev Emitted when a round is cancelled for any reason
     */
    event CancelRound(MarketId indexed id, uint256 indexed roundId);

    /**
     * @dev Emitted whenever a new market is created successfully
     */
    event CreateMarket(MarketId indexed id, address creator);

    /**
     * @dev Emiited when a user opens a bearish position on a market
     */
    event Bearish(
        MarketId indexed id, uint256 indexed roundId, address indexed account, bytes32 positionId, uint256 stake
    );

    /**
     * @dev Emitted when a user opens a bullish position on a market
     */
    event Bullish(
        MarketId indexed id, uint256 indexed roundId, address indexed account, bytes32 positionId, uint256 stake
    );

    /**
     * @dev Emitted when a user settles a position
     */
    event Settle(
        MarketId indexed id, uint256 indexed roundId, address indexed account, bytes32 positionId, uint256 reward
    );

    /**
     * @dev Emitted when a user claims refund refund from a cancelled round
     */
    event ClaimRefund(
        MarketId indexed id, uint256 indexed roundId, address indexed account, bytes32 positionId, uint256 stake
    );

    /// @notice Emitted when native/local tokens are recovered from the contract
    event RecoverToken(Currency indexed currency, address indexed recipient, uint256 amount);
}
          

src/interfaces/IGrailMarket.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {MarketId} from "../types/MarketId.sol";

interface IGrailMarket {
    /**
     * @dev Allows anyone to resolve a prediction round
     *
     * @param id market identifier
     * @param roundId id of round to finalize
     * @param priceUpdate pyth price update data
     */
    function resolve(MarketId id, uint256 roundId, bytes calldata priceUpdate) external payable;

    /**
     * @dev Allows protocol managers to set incentive fee for market round resolvers
     *
     * @notice As a permissionless decentralized protocol, we have resorted to incentivize
     * market resolvers. Anyone who successfully resolves a round, is entitled
     * to earn a rebate percentage of accrued protocol fee in that round.
     *
     * @param fee the fee in BPS
     */
    function setResolverFee(uint16 fee) external;
}
          

src/interfaces/external/IERC20.sol

// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity ^0.8.0;

/**
 * @title IERC20
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

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

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

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

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

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

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

src/libraries/Market.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {MarketId} from "../types/MarketId.sol";
import {Position} from "./Position.sol";

library Market {
    /// @dev possible state of a round
    enum RoundStatus {
        NOT_OPEN,
        OPEN,
        LIVE,
        RESOLVED,
        REFUNDING
    }

    /**
     * @notice A structure defining a market uniquely
     */
    struct Config {
        /// the minimum amount that can be staked
        uint176 minStakeAmount;
        /// the duration of each market round in seconds
        uint48 duration;
        /// the incentive fee rewarded by this market for settling a round
        uint16 incentiveFeeBps;
        /// the protocol fee charged on this market
        uint16 protocolFeeBps;
    }

    /// @dev structure for market prediction round
    struct Round {
        /// unix timestamp of when market round opens
        uint64 openingTime;
        /// unix timestamp of when market round is due for finaliztion
        uint64 closingTime;
        /// asset closing price as obtained from the oracle
        int64 closingPrice;
        /// round locked price
        int64 priceMark;
        /// sum of all stakes in this round
        uint128 totalShares;
        /// sum of all bullish positions stakes
        uint128 bullShares;
        /// sum of all bearish positions stakes
        uint128 bearShares;
        /// total amount available for disbursment as reward
        uint128 rewardPool;
        /// total winning stakes
        uint128 winningShares;
        /// the status describing the round state
        RoundStatus status;
    }

    struct MarketInfo {
        /// keeps track of market last round id
        uint256 roundId;
        /// Pyth Network oracle price feed identifier
        bytes32 oracleId;
        /// keeps track of users positions in this market
        mapping(bytes32 positionId => Position.State) positions;
        /// keeps track of market rounds
        mapping(uint256 roundId => Round) rounds;
    }
}
          

src/libraries/Option.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

type Option is uint8;

library OptionLibrary {
    using OptionLibrary for Option;

    Option public constant BEARISH = Option.wrap(0);
    Option public constant BULLISH = Option.wrap(1);

    function isBearish(Option option) internal pure returns (bool) {
        return Option.unwrap(option) == Option.unwrap(BEARISH);
    }

    function isBullish(Option option) internal pure returns (bool) {
        return Option.unwrap(option) == Option.unwrap(BULLISH);
    }
}
          

src/libraries/Position.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {MarketId} from "../types/MarketId.sol";
import {Option, OptionLibrary} from "./Option.sol";

library Position {
    using OptionLibrary for Option;

    /**
     * @dev State represents a structure for users current
     * prediction
     */
    struct State {
        // The amount staked as wager for position
        uint240 stake;
        // The user's choice of either `Bearish` or `Bullish`
        Option option;
        // True if the option has been exercised or position reward claimed
        bool settled;
    }

    /**
     * @dev Returns true if a users position won otherwise false
     *
     * @param position The user position
     * @param closingPrice the market closing price
     * @param markPrice the prediction round mark price
     * @return rewardable true if position expired in the money
     */
    function isRewardable(State memory position, int64 closingPrice, int64 markPrice)
        internal
        pure
        returns (bool rewardable)
    {
        if (position.option.isBullish()) {
            rewardable = closingPrice > markPrice;
        } else {
            rewardable = closingPrice < markPrice;
        }
    }

    /**
     * @dev Abi encoded `marketId`, `sequenceId` and `owner`
     *
     * @param id unique market identifier
     * @param sequenceId incremented sequence Id
     * @param owner address of Postion taker
     *
     * @return positionId Unique id for the users position in round
     */
    function toId(MarketId id, uint256 sequenceId, address owner) internal pure returns (bytes32 positionId) {
        positionId = keccak256(abi.encodePacked(id, sequenceId, owner));
    }
}
          

src/types/Currency.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import {IERC20} from "../interfaces/external/IERC20.sol";

type Currency is address;

using {equals as ==} for Currency global;
using CurrencyLibrary for Currency global;

function equals(Currency currency, Currency other) pure returns (bool) {
    return Currency.unwrap(currency) == Currency.unwrap(other);
}

/**
 * @title CurrencyLibrary
 * @dev This library allows for transferring and holding native tokens and ERC20 tokens
 */
library CurrencyLibrary {
    /**
     * @notice Thrown when native transfer fails
     */
    error NativeTransferFailed();

    /// @notice Thrown when an ERC20 transfer fails
    error ERC20TransferFailed();

    /**
     * @notice Thrown when an ERC20 TrasnaferFrom fails
     */
    error ERC20TransferFromFailed();

    /// @notice A constant to represent the native currency
    Currency public constant ADDRESS_ZERO = Currency.wrap(address(0));

    function nativeTransfer(address to, uint256 amount) internal {
        bool success;

        assembly ("memory-safe") {
            // Transfer the ETH and revert if it fails.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }
        // revert with NativeTransferFailed
        if (!success) revert NativeTransferFailed();
    }

    function tokenTransfer(Currency currency, address to, uint256 amount) internal {
        bool success;

        assembly ("memory-safe") {
            // Get a pointer to some free memory.
            let fmp := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(fmp, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(fmp, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(fmp, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success :=
                and(
                    // Set success to whether the call reverted, if not we check it either
                    // returned exactly 1 (can't just be non-zero data), or had no return data.
                    or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                    // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                    // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                    // Counterintuitively, this call must be positioned second to the or() call in the
                    // surrounding and() call or else returndatasize() will be zero during the computation.
                    call(gas(), currency, 0, fmp, 68, 0, 32)
                )

            // Now clean the memory we used
            mstore(fmp, 0) // 4 byte `selector` and 28 bytes of `to` were stored here
            mstore(add(fmp, 0x20), 0) // 4 bytes of `to` and 28 bytes of `amount` were stored here
            mstore(add(fmp, 0x40), 0) // 4 bytes of `amount` were stored here
        }
        // revert with ERC20TransferFailed
        if (!success) revert ERC20TransferFailed();
    }

    function transfer(Currency currency, address to, uint256 amount) internal {
        // altered from https://github.com/transmissions11/solmate/blob/44a9963d4c78111f77caa0e65d677b8b46d6f2e6/src/utils/SafeTransferLib.sol
        // modified custom error selectors

        if (currency.isNative()) {
            nativeTransfer(to, amount);
        } else {
            tokenTransfer(currency, to, amount);
        }
    }

    function safeTransferFrom(Currency currency, address from, address to, uint256 amount) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success :=
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), currency, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }

        // revert with ERC20TransferFromFailed
        if (!success) revert ERC20TransferFromFailed();
    }

    function balanceOfSelf(Currency currency) internal view returns (uint256) {
        if (currency.isNative()) {
            return address(this).balance;
        } else {
            return IERC20(Currency.unwrap(currency)).balanceOf(address(this));
        }
    }

    function isNative(Currency currency) internal pure returns (bool) {
        return Currency.unwrap(currency) == Currency.unwrap(ADDRESS_ZERO);
    }
}
          

src/types/MarketId.sol

// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

type MarketId is bytes32;

function equals(MarketId id, MarketId otherId) pure returns (bool) {
    return MarketId.unwrap(id) == MarketId.unwrap(otherId);
}

function notEquals(MarketId id, MarketId otherId) pure returns (bool) {
    return MarketId.unwrap(id) == MarketId.unwrap(otherId);
}

using { equals as ==, notEquals as != } for MarketId global;
          

Compiler Settings

{"viaIR":true,"remappings":["forge-std/=lib/forge-std/src/","solady/=lib/solady/src/","@pythnetwork/=node_modules/@pythnetwork/","hardhat/=node_modules/hardhat/","wormhole-solidity-sdk/=lib/wormhole-solidity-sdk/src/","ds-test/=lib/wormhole-solidity-sdk/lib/forge-std/lib/ds-test/src/"],"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"]}},"optimizer":{"runs":44444444,"enabled":true},"metadata":{"useLiteralContent":false,"bytecodeHash":"ipfs","appendCBOR":true},"libraries":{},"evmVersion":"paris"}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_owner","internalType":"address"},{"type":"address","name":"_pyth","internalType":"address"},{"type":"address","name":"_currency","internalType":"Currency"}]},{"type":"error","name":"ActionTooEarly","inputs":[]},{"type":"error","name":"AlreadyInitialized","inputs":[]},{"type":"error","name":"CannotRecoverCurrency","inputs":[]},{"type":"error","name":"CannotStakeZeroAmount","inputs":[]},{"type":"error","name":"ERC20TransferFailed","inputs":[]},{"type":"error","name":"ERC20TransferFromFailed","inputs":[]},{"type":"error","name":"EntryNotAllowed","inputs":[]},{"type":"error","name":"FeeTooLarge","inputs":[{"type":"uint16","name":"fee","internalType":"uint16"}]},{"type":"error","name":"InsufficientBalance","inputs":[]},{"type":"error","name":"InsufficientFee","inputs":[]},{"type":"error","name":"InvalidRoundStatus","inputs":[]},{"type":"error","name":"MarketAlreadyExist","inputs":[]},{"type":"error","name":"MarketDoesNotExist","inputs":[]},{"type":"error","name":"NativeTransferFailed","inputs":[]},{"type":"error","name":"NewOwnerIsZeroAddress","inputs":[]},{"type":"error","name":"NoHandoverRequest","inputs":[]},{"type":"error","name":"PositionAlreadyExist","inputs":[]},{"type":"error","name":"PositionNotFound","inputs":[]},{"type":"error","name":"RewardClaimed","inputs":[]},{"type":"error","name":"SorryNoReward","inputs":[]},{"type":"error","name":"Unauthorized","inputs":[]},{"type":"event","name":"Bearish","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId","indexed":true},{"type":"uint256","name":"roundId","internalType":"uint256","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"bytes32","name":"positionId","internalType":"bytes32","indexed":false},{"type":"uint256","name":"stake","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Bullish","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId","indexed":true},{"type":"uint256","name":"roundId","internalType":"uint256","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"bytes32","name":"positionId","internalType":"bytes32","indexed":false},{"type":"uint256","name":"stake","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"CancelRound","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId","indexed":true},{"type":"uint256","name":"roundId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"ClaimRefund","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId","indexed":true},{"type":"uint256","name":"roundId","internalType":"uint256","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"bytes32","name":"positionId","internalType":"bytes32","indexed":false},{"type":"uint256","name":"stake","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"CollectFee","inputs":[{"type":"address","name":"currency","internalType":"Currency","indexed":true},{"type":"address","name":"collector","internalType":"address","indexed":true},{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"CreateMarket","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId","indexed":true},{"type":"address","name":"creator","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"NewRound","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId","indexed":true},{"type":"uint256","name":"roundId","internalType":"uint256","indexed":true},{"type":"uint256","name":"openingTime","internalType":"uint256","indexed":false},{"type":"uint256","name":"closingTime","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipHandoverCanceled","inputs":[{"type":"address","name":"pendingOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipHandoverRequested","inputs":[{"type":"address","name":"pendingOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"oldOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"RecoverToken","inputs":[{"type":"address","name":"currency","internalType":"Currency","indexed":true},{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Resolve","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId","indexed":true},{"type":"uint256","name":"roundId","internalType":"uint256","indexed":true},{"type":"uint256","name":"rewardPool","internalType":"uint256","indexed":false},{"type":"uint256","name":"totalWinningStake","internalType":"uint256","indexed":false},{"type":"int256","name":"closingPrice","internalType":"int256","indexed":false},{"type":"uint256","name":"winningSide","internalType":"uint256","indexed":false},{"type":"bool","name":"isRefunding","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"SetMarketDuration","inputs":[{"type":"uint32","name":"duration","internalType":"uint32","indexed":false}],"anonymous":false},{"type":"event","name":"SetMinStakeAmount","inputs":[{"type":"uint256","name":"minStakeAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetProtocolFee","inputs":[{"type":"uint16","name":"newFee","internalType":"uint16","indexed":false}],"anonymous":false},{"type":"event","name":"SetResolverFee","inputs":[{"type":"uint16","name":"newFee","internalType":"uint16","indexed":false}],"anonymous":false},{"type":"event","name":"SetRoundPriceMark","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId","indexed":true},{"type":"uint256","name":"roundId","internalType":"uint256","indexed":true},{"type":"int256","name":"priceMark","internalType":"int256","indexed":false},{"type":"uint256","name":"closingTime","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Settle","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId","indexed":true},{"type":"uint256","name":"roundId","internalType":"uint256","indexed":true},{"type":"address","name":"account","internalType":"address","indexed":true},{"type":"bytes32","name":"positionId","internalType":"bytes32","indexed":false},{"type":"uint256","name":"reward","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"BASIS_POINT","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"CAN_CANCEL_AFTER","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint16","name":"","internalType":"uint16"}],"name":"MAX_PROTOCOL_FEE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"MAX_SECONDS_OFFSET","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"bearish","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256","name":"roundId","internalType":"uint256"},{"type":"uint256","name":"stake","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"bullish","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256","name":"roundId","internalType":"uint256"},{"type":"uint256","name":"stake","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"cancelOwnershipHandover","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"cancelRound","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256","name":"roundId","internalType":"uint256"},{"type":"bytes","name":"priceUpdate","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claimRefund","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256","name":"roundId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"collectProtocolFee","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"completeOwnershipHandover","inputs":[{"type":"address","name":"pendingOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint176","name":"minStakeAmount","internalType":"uint176"},{"type":"uint48","name":"duration","internalType":"uint48"},{"type":"uint16","name":"incentiveFeeBps","internalType":"uint16"},{"type":"uint16","name":"protocolFeeBps","internalType":"uint16"}],"name":"config","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"createMarket","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"bytes","name":"priceUpdate","internalType":"bytes"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"Currency"}],"name":"currency","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"r","internalType":"struct Position.State","components":[{"type":"uint240","name":"stake","internalType":"uint240"},{"type":"uint8","name":"option","internalType":"Option"},{"type":"bool","name":"settled","internalType":"bool"}]}],"name":"getPositionInfo","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256","name":"roundId","internalType":"uint256"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"r","internalType":"struct Market.Round","components":[{"type":"uint64","name":"openingTime","internalType":"uint64"},{"type":"uint64","name":"closingTime","internalType":"uint64"},{"type":"int64","name":"closingPrice","internalType":"int64"},{"type":"int64","name":"priceMark","internalType":"int64"},{"type":"uint128","name":"totalShares","internalType":"uint128"},{"type":"uint128","name":"bullShares","internalType":"uint128"},{"type":"uint128","name":"bearShares","internalType":"uint128"},{"type":"uint128","name":"rewardPool","internalType":"uint128"},{"type":"uint128","name":"winningShares","internalType":"uint128"},{"type":"uint8","name":"status","internalType":"enum Market.RoundStatus"}]},{"type":"tuple","name":"p","internalType":"struct Position.State","components":[{"type":"uint240","name":"stake","internalType":"uint240"},{"type":"uint8","name":"option","internalType":"Option"},{"type":"bool","name":"settled","internalType":"bool"}]}],"name":"getRoundInfo","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256","name":"roundId","internalType":"uint256"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"roundId","internalType":"uint256"},{"type":"bytes32","name":"oracleId","internalType":"bytes32"}],"name":"markets","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"result","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"result","internalType":"uint256"}],"name":"ownershipHandoverExpiresAt","inputs":[{"type":"address","name":"pendingOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"protocolFeesAccrued","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IPyth"}],"name":"pyth","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverToken","inputs":[{"type":"address","name":"token","internalType":"Currency"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"requestOwnershipHandover","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"resolve","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256","name":"roundId","internalType":"uint256"},{"type":"bytes","name":"priceUpdate","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMarketDuration","inputs":[{"type":"uint32","name":"duration","internalType":"uint32"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMinStakeAmount","inputs":[{"type":"uint256","name":"minStakeAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setProtocolFee","inputs":[{"type":"uint16","name":"fee","internalType":"uint16"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setResolverFee","inputs":[{"type":"uint16","name":"fee","internalType":"uint16"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"settle","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256","name":"roundId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"settleBatch","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256[]","name":"roundIds","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"settleFor","inputs":[{"type":"bytes32","name":"id","internalType":"MarketId"},{"type":"uint256","name":"roundId","internalType":"uint256"},{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tvl","inputs":[]}]
              

Contract Creation Code

0x60c0346200012957601f6200324138819003918201601f19168301916001600160401b038311848410176200012e5780849260609460405283398101031262000129576200004d8162000144565b906200006a6040620000626020840162000144565b920162000144565b6080526001600160a01b03918216638b78c6d81981905560007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a31660a052600380546001600160b01b0316687d007d00000000004b60b21b1790556040516130e790816200015a8239608051818181610ca401528181610ec8015281816111560152818161143d0152818161198d01528181611c0401528181611d4501528181611eec015261265d015260a05181818161056d015261296a0152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001295756fe60808060405260048036101561001457600080fd5b60003560e01c91826310aa4e8f14611f105750816312b437db14611ec357816320758f6414611df85781632569296214611d8e578163347ec17d14611cb857816335b00f1d14611c7e5781633cff986014611b9a57816342dcf2a214611acb57816354d1f13d14611a655781635a15b573146118a85781635d5910c41461166d578163715018a6146115ee5781637564912b1461159d57816375f2448d146114de57816379502c551461146357816379f48d4c146114175781638da5cb5b146113a65781638da9fa271461136b578163972571ae146111e6578163a7229fd9146110d5578163ada5f6421461109a578163b621e75a1461105e578163b8ca3b8314611023578163e4467f3514610f28578163e5328e0614610eec578163e5a6b10f14610e7d578163e79d83ab14610a36578163eb4af0451461098f578163ef9162861461070a578163f04e283e14610643578163f2fde38b14610591578163f98d06f014610522578163fad71835146101ed575063fee81cf41461019757600080fd5b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576101ce6120ce565b63389a75e1600c52600052602080600c2054604051908152f35b600080fd5b346101e8576101fb366120f1565b600061012060405161020c81612305565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152015261024a61237f565b50826000526001602052604060002091806000526003830160205260406000209360036040519561027a87612305565b805467ffffffffffffffff8116885267ffffffffffffffff8160401c1660208901528060801c60070b604089015260c01d60070b606088015260018101546fffffffffffffffffffffffffffffffff8116608089015260801c60a088015260028101546fffffffffffffffffffffffffffffffff811660c089015260801c60e088015201546fffffffffffffffffffffffffffffffff8116610100870152600560ff8260801c1610156104f45791600294939160ff6103429460801c16610120880152612ec6565b600052016020526040600020916040519261035c846122ba565b547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116845260ff8160f01c16602085015260f81c151560408401526101206040519267ffffffffffffffff815116845267ffffffffffffffff6020820151166020850152604081015160070b6040850152606081015160070b60608501526fffffffffffffffffffffffffffffffff60808201511660808501526fffffffffffffffffffffffffffffffff60a08201511660a08501526fffffffffffffffffffffffffffffffff60c08201511660c08501526fffffffffffffffffffffffffffffffff60e08201511660e08501526fffffffffffffffffffffffffffffffff6101008201511661010085015201519060058210156104c6575061012082015281517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16610140820152602082015160ff1661016082015260409091015115156101808201526101a090f35bf35b6021907f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b6021877f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576105c36120ce565b6105cb6121fd565b8060601b156106365773ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355005b50637448fbae600052601cfd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576106756120ce565b61067d6121fd565b63389a75e1600c52806000526020600c2091825442116106fe5750600073ffffffffffffffffffffffffffffffffffffffff9255167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355005b636f5e8818600052601cfd5b61071336612096565b6fffffffffffffffffffffffffffffffff92919293848216836000526001602052604060002095856000526003870160205260406000206001880154156109665760ff600382015460801c1660058110156109385760010361090f5782156108e6576002610782338989612ec6565b9889600052016020526040600020937dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8554166108be57507e01000000000000000000000000000000000000000000000000000000000000929160016108589201908382547fffffffffffffffffffffffffffffffff000000000000000000000000000000008383818416011691161780845560801c01166fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b7fff000000000000000000000000000000000000000000000000000000000000008354161717905561088b348233612652565b60405193845260208401527f76be093b9f8b5c898a6038f68c84b4dc903bc2fbefdc455a35a86fbe0875acc860403394a4005b6040517f68cf1349000000000000000000000000000000000000000000000000000000008152fd5b836040517f3c4a9486000000000000000000000000000000000000000000000000000000008152fd5b836040517f0ea6e1e7000000000000000000000000000000000000000000000000000000008152fd5b6021857f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b836040517fb0cfa447000000000000000000000000000000000000000000000000000000008152fd5b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760207f6e4bcc82cb4b918df6b911c8092c49f027b7d409733e2c97bb0a4b5a367e19db91356109eb6121fd565b75ffffffffffffffffffffffffffffffffffffffffffff81167fffffffffffffffffffff000000000000000000000000000000000000000000006003541617600355604051908152a1005b610a3f366121a8565b83600095929395526001918260205260406000208660005260038101602052604060002092600384019560ff875460801c16600581101561093857600203610e545767ffffffffffffffff9182865460401c1691428311610e2b5787850154600a8401948511610dfd5790610ab694939291612895565b509184880190818911610dcf575091610b018783610afc8460a09997847fac40c95b92f792b83170dfdb011f9e519b63f5990b694950d73c48ca504dd52f9c9a98612d96565b612c4d565b600092600060028401906fffffffffffffffffffffffffffffffff9283835416151580610dc0575b15610d385780860184815416928560035494610bbd610b7c610b5c84610b6961ffff8b8361271095869260f01c8a612271565b16049b60e01c168b612271565b160497610b768982612298565b93612298565b88546fffffffffffffffffffffffffffffffff1660809190911b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000016178855565b1660025401600255875460c01d60070b8760070b818113600014610cf15750505091610c98868693610cc8955460801c7fffffffffffffffffffffffffffffffff000000000000000000000000000000008d5416178c55985b7003000000000000000000000000000000007fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff8d5416178c55907fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff82549160801b77ffffffffffffffff00000000000000000000000000000000169116179055565b168060005403600055337f0000000000000000000000000000000000000000000000000000000000000000612f26565b5460801c945416604051948552602085015260070b6040840152606083015215156080820152a3005b879450610cc8959350988891610c98939a1215610c1657848754167fffffffffffffffffffffffffffffffff000000000000000000000000000000008d5416178c55610c16565b87547fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff1670040000000000000000000000000000000017885585547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1677ffffffffffffffff00000000000000000000000000000000608087901b1617909555939450610cc8565b508086015460801c1515610b29565b6011907f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b6011877f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b856040517fde8f5316000000000000000000000000000000000000000000000000000000008152fd5b836040517fc38434ab000000000000000000000000000000000000000000000000000000008152fd5b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576020600054604051908152f35b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857803561ffff8116918282036101e857610f6e6121fd565b6103e88311610ff3577f5aa89d2018da401c466211564e2994895ca1dc68df9d55db7482e437dadc0811602084847dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffff0000000000000000000000000000000000000000000000000000000000006003549260f01b16911617600355604051908152a1005b60249083604051917f35a30751000000000000000000000000000000000000000000000000000000008352820152fd5b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760206040516103e88152f35b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576020600254604051908152f35b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760206040516127108152f35b346101e85760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85761110c6120ce565b9060243573ffffffffffffffffffffffffffffffffffffffff91828216938483036101e85761114760443580946111416121fd565b83612f26565b836111518261300e565b9116937f000000000000000000000000000000000000000000000000000000000000000016841490816111da575b506111b2575060207f16a1412f01b73c390eb2548427101644aa86c1443c272f73df00fb74c48fe49991604051908152a3005b6040517ff4d678b8000000000000000000000000000000000000000000000000000000008152fd5b9050600054118561117f565b6111ef366121a8565b83600095929395526001602052604060002092856000526003840160205260406000209160038301805460ff8160801c1660058110156104f4576002036113425767ffffffffffffffff80955460401c166103848101809111610dfd574210610e2b577fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff70040000000000000000000000000000000091161790556001850154917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc4420193428511611314576112cd94428216949116929190612895565b50906001850190818611610dcf575091610afc84926112ed948484612d96565b7f4be96238c3feeb1d8156e058500c5d1f6d147bb1c234e2019998bfbabe2b4a5c600080a3005b6011867f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b856040517fc38434ab000000000000000000000000000000000000000000000000000000008152fd5b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760206040516103848152f35b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346101e85761146161143261142b36612146565b339161239e565b8060005403600055337f0000000000000000000000000000000000000000000000000000000000000000612f26565b005b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760806003546040519075ffffffffffffffffffffffffffffffffffffffffffff8116825265ffffffffffff8160b01c16602083015261ffff8160e01c16604083015260f01c6060820152f35b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8573563ffffffff8116908181036101e8577f50e5c1c885ea2510512f889012b87e95d7696281a6c2985c6c7d45fda6d792d59160209161154a6121fd565b7fffffffff000000000000ffffffffffffffffffffffffffffffffffffffffffff79ffffffff000000000000000000000000000000000000000000006003549260b01b16911617600355604051908152a1005b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8573560005260016020526040806000206001815491015482519182526020820152f35b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576116206121fd565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a355005b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85780356024359167ffffffffffffffff928381116101e8576116bb903690830161217a565b9390918360005260016020526040600020926001840195865461187f577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed4420192428411610dcf575061146196848761174381967fccd5d394b6c44121d18f7dc9af1472e400cd79e3318c578aca4eeacb5d9df7a4966040968560019b421693169185612895565b509255847fb695ede53322ecf2e50fbc58c9cd70baa3835819be7d0c523a69efcab3c44bf560208551338152a261177a8588612c4d565b856000526003870160205282600020906117a165ffffffffffff60035460b01c1642612235565b1690600381017002000000000000000000000000000000007fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff8254161790556118268282907fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff000000000000000083549260401b169116179055565b805477ffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffff0000000000000000000000000000000000000000000000008560c01b16911617905582519160070b82526020820152a3612c4d565b826040517fde3c363f000000000000000000000000000000000000000000000000000000008152fd5b346101e8576118b636612146565b908060005260016020526040600020928260005260038401602052604060002060026118e3338686612ec6565b95866000520160205260ff6003604060002092015460801c166005811015611a37578203611a0e578054918260f81c6119e657507f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83161781556119b17dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8093168060005403600055337f0000000000000000000000000000000000000000000000000000000000000000612f26565b541660405193845260208401527ffb75c09f4826c289f841e35b10c86b0e385d3f01ff49cfc12d5626d24645f64a60403394a4005b6040517f4f9e331b000000000000000000000000000000000000000000000000000000008152fd5b506040517fc38434ab000000000000000000000000000000000000000000000000000000008152fd5b6021837f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85763389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2005b346101e8576002611aff611ade366120f1565b90611aea93929361237f565b50836000526001602052604060002093612ec6565b600052016020526060604060002060405190611b1a826122ba565b547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116825260ff8160f01c16602083015260f81c151560408201526104c46040518092604080917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff815116845260ff602082015116602085015201511515910152565b346101e85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857611bd16120ce565b90602435611bdd6121fd565b80611c785750600254905b60025490828203918211610dcf575060025580600054036000557f000000000000000000000000000000000000000000000000000000000000000090611c2f818484612f26565b6040519081527f4faa6fbacd77a523e80ea39326e5d792fb633131f897c119ec498d60f4bd68f9602073ffffffffffffffffffffffffffffffffffffffff8095169433941692a4005b90611be8565b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576020604051600a8152f35b346101e85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857602480359167ffffffffffffffff908035908285116101e857366023860112156101e8578401359182116101e857600590368484841b870101116101e8579291906000936000935b838510611d6957611461868060005403600055337f0000000000000000000000000000000000000000000000000000000000000000612f26565b9091929394506001611d83338388871b8a0101358561239e565b950193929190611d2f565b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85763389a75e1600c52336000526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a2005b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857803561ffff8116918282036101e857611e3e6121fd565b6103e88311610ff3577f5aa89d2018da401c466211564e2994895ca1dc68df9d55db7482e437dadc0811602084847fffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffff000000000000000000000000000000000000000000000000000000006003549260e01b16911617600355604051908152a1005b346101e857611461611ee1611ed7366120f1565b929190839161239e565b9081600054036000557f0000000000000000000000000000000000000000000000000000000000000000612f26565b90611f1a36612096565b90926fffffffffffffffffffffffffffffffff9485831690846000526001602052604060002096866000526003880160205260406000209160018901541561206f575060ff600383015460801c1660058110156109385760010361090f5782156108e6576002611f8b338989612ec6565b9889600052016020526040600020937dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8554166108be5750600260018301928354937fffffffffffffffffffffffffffffffff000000000000000000000000000000009485858881841601169116179055019182549184818416011691161790557fff0000000000000000000000000000000000000000000000000000000000000082541617905561203c348233612652565b60405193845260208401527facad006dfb05c480e68abb20c66f13c27f5971dd27fb0e3fa9e4f0f6d1a3abee60403394a4005b807fb0cfa44700000000000000000000000000000000000000000000000000000000869252fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101e857600435906024359060443590565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101e857565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101e857600435906024359060443573ffffffffffffffffffffffffffffffffffffffff811681036101e85790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60409101126101e8576004359060243590565b9181601f840112156101e85782359167ffffffffffffffff83116101e857602083818601950101116101e857565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101e85760043591602435916044359067ffffffffffffffff82116101e8576121f99160040161217a565b9091565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754330361222757565b6382b429006000526004601cfd5b9190820180921161224257565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9190916fffffffffffffffffffffffffffffffff8080941691160291821691820361224257565b6fffffffffffffffffffffffffffffffff918216908216039190821161224257565b6060810190811067ffffffffffffffff8211176122d657604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff8211176122d657604052565b6040810190811067ffffffffffffffff8211176122d657604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176122d657604052565b6040519061238c826122ba565b60006040838281528260208201520152565b909291926000918083526001602052604080842093838152600385016020528181209060026123ce898787612ec6565b9687835201602052828120916003810160ff815460801c166005811015612625576003036125fc578354917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff948584169384156125d3578060f81c6125aa577effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0100000000000000000000000000000000000000000000000000000000000000179081905560f01c60ff166001036125945780548060c01d60070b9060801c60070b135b1561256b576002015460801c84818402169283040361253e57546fffffffffffffffffffffffffffffffff16918215612511575083519687520416602085018190529573ffffffffffffffffffffffffffffffffffffffff16937f31295a2f764b13b07abb49b6fd4fe8a7ca220b09c72570d04cd947871c999d1a9190a4565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526012600452fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b600486517f4f97cab6000000000000000000000000000000000000000000000000000000008152fd5b80548060c01d60070b9060801c60070b12612491565b600488517f4f9e331b000000000000000000000000000000000000000000000000000000008152fd5b600488517f6ec9be11000000000000000000000000000000000000000000000000000000008152fd5b600485517fc38434ab000000000000000000000000000000000000000000000000000000008152fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6000805483018155907f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff82166126cc57505050036126a257565b60046040517f025dbdd4000000000000000000000000000000000000000000000000000000008152fd5b82939450601c8360209493606493604051986060523060405260601b602c526f23b872dd000000000000000000000000600c525af13d156001835114171690606052816040521561271a5750565b807fa512d51e0000000000000000000000000000000000000000000000000000000060049252fd5b80511561274f5760200190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9080825180825260208092019180808360051b86010195019360009384915b8483106127ae575050505050505090565b90919293949596847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08084840301855289518051908185528a5b82811061281457505080840183018a9052601f011690910181019781019695949360010192019061279d565b8181018501518682018601528994016127e8565b91908260809103126101e85760405167ffffffffffffffff9260808201848111838210176122d657604052819381518060070b81036101e8578352602082015190811681036101e857602083015260408101518060030b81036101e8576040830152606090810151910152565b92939190916040938451906128a982612322565b60019283835260209788840192893685376128c385612742565b528751966128d088612322565b8588528960005b818110612c3d57505067ffffffffffffffff968781116122d6578951916129258c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116018461233e565b81835236828201116101e857816000928d92838601378301015261294888612742565b5261295287612742565b5073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016948851947fd47eed450000000000000000000000000000000000000000000000000000000086528a60048701528a86806129c7602482018d61277e565b03818a5afa958615612c3257600096612c00575b50853410612bd757918792612a2899969594928c8c519b8c997f4716e9c5000000000000000000000000000000000000000000000000000000008b52608060048c015260848b019061277e565b8981037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0160248b015292518084529201959160008e5b828210612bbd5750505050908316604487015250166064840152600093908390039183915af1918215612bb257600092612ab1575b5050612aa09150612742565b510151906060825160070b92015190565b90913d8082843e612ac2818461233e565b8201918581840312612b7d57805190848211612bae57019180601f84011215612b7d578251938411612b8157845194612b00878660051b018761233e565b848652868601928761012080970286010194838611612b7d578801935b858510612b365750505050505050612aa0903880612a94565b8685850312612b7d578887918451612b4d816122ba565b87518152612b5d87848a01612828565b83820152612b6e8760a08a01612828565b86820152815201940193612b1d565b5080fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8280fd5b83513d6000823e3d90fd5b845189528d9a50978801978c97509093019282018e612a5f565b60048a517f025dbdd4000000000000000000000000000000000000000000000000000000008152fd5b90958b82813d8311612c2b575b612c17818361233e565b81010312612c2857505194386129db565b80fd5b503d612c0d565b8a513d6000823e3d90fd5b606082828c010152018a906128d7565b9081547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114612242576001019182815567ffffffffffffffff9081421660035460af1c6601fffffffffffe65fffffffffffe82169116810361224257810192808411612242576000868152600393840160205260409081902093840180547fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000017905583547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff84167fffffffffffffffffffffffffffffffff000000000000000000000000000000009091161785821b6fffffffffffffffff000000000000000016179093557f30745dda6a7b321538e5fc06390354a4f93646c86e3d8d29dad0b0beeff58098938351928352166020820152a3565b7fccd5d394b6c44121d18f7dc9af1472e400cd79e3318c578aca4eeacb5d9df7a4919293600360409286600052016020528160002067ffffffffffffffff612deb65ffffffffffff60035460b01c1642612235565b1690600381017002000000000000000000000000000000007fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff825416179055612e708282907fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff000000000000000083549260401b169116179055565b805477ffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffff0000000000000000000000000000000000000000000000008560c01b16911617905582519160070b82526020820152a3565b917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000906040519260208401948552604084015260601b166060820152605481526080810181811067ffffffffffffffff8211176122d65760405251902090565b73ffffffffffffffffffffffffffffffffffffffff90808216612f7f575050600080809381935af115612f5557565b60046040517ff4b3b1bc000000000000000000000000000000000000000000000000000000008152fd5b6020906040929483519586957fa9059cbb000000000000000000000000000000000000000000000000000000008752166004860152602485015260446000948580935af13d15601f3d11600185511416171692828152826020820152015215612fe457565b60046040517ff27f64e4000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff168061302e57504790565b6020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa9081156130a557600091613077575090565b906020823d821161309d575b816130906020938361233e565b81010312612c2857505190565b3d9150613083565b6040513d6000823e3d90fdfea2646970667358221220c8b84f57112d19e1a7c44a61808e583205ee5d1dc30194a4ae7e43c7ff74620d64736f6c63430008140033000000000000000000000000bcb5fd84cc71cedc15b4651f18d912f816b61d330000000000000000000000002880ab155794e7179c9ee2e38200202908c17b430000000000000000000000000000000000000000000000000000000000000000

Deployed ByteCode

0x60808060405260048036101561001457600080fd5b60003560e01c91826310aa4e8f14611f105750816312b437db14611ec357816320758f6414611df85781632569296214611d8e578163347ec17d14611cb857816335b00f1d14611c7e5781633cff986014611b9a57816342dcf2a214611acb57816354d1f13d14611a655781635a15b573146118a85781635d5910c41461166d578163715018a6146115ee5781637564912b1461159d57816375f2448d146114de57816379502c551461146357816379f48d4c146114175781638da5cb5b146113a65781638da9fa271461136b578163972571ae146111e6578163a7229fd9146110d5578163ada5f6421461109a578163b621e75a1461105e578163b8ca3b8314611023578163e4467f3514610f28578163e5328e0614610eec578163e5a6b10f14610e7d578163e79d83ab14610a36578163eb4af0451461098f578163ef9162861461070a578163f04e283e14610643578163f2fde38b14610591578163f98d06f014610522578163fad71835146101ed575063fee81cf41461019757600080fd5b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576101ce6120ce565b63389a75e1600c52600052602080600c2054604051908152f35b600080fd5b346101e8576101fb366120f1565b600061012060405161020c81612305565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e082015282610100820152015261024a61237f565b50826000526001602052604060002091806000526003830160205260406000209360036040519561027a87612305565b805467ffffffffffffffff8116885267ffffffffffffffff8160401c1660208901528060801c60070b604089015260c01d60070b606088015260018101546fffffffffffffffffffffffffffffffff8116608089015260801c60a088015260028101546fffffffffffffffffffffffffffffffff811660c089015260801c60e088015201546fffffffffffffffffffffffffffffffff8116610100870152600560ff8260801c1610156104f45791600294939160ff6103429460801c16610120880152612ec6565b600052016020526040600020916040519261035c846122ba565b547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116845260ff8160f01c16602085015260f81c151560408401526101206040519267ffffffffffffffff815116845267ffffffffffffffff6020820151166020850152604081015160070b6040850152606081015160070b60608501526fffffffffffffffffffffffffffffffff60808201511660808501526fffffffffffffffffffffffffffffffff60a08201511660a08501526fffffffffffffffffffffffffffffffff60c08201511660c08501526fffffffffffffffffffffffffffffffff60e08201511660e08501526fffffffffffffffffffffffffffffffff6101008201511661010085015201519060058210156104c6575061012082015281517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff16610140820152602082015160ff1661016082015260409091015115156101808201526101a090f35bf35b6021907f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b6021877f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002880ab155794e7179c9ee2e38200202908c17b43168152f35b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576105c36120ce565b6105cb6121fd565b8060601b156106365773ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355005b50637448fbae600052601cfd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576106756120ce565b61067d6121fd565b63389a75e1600c52806000526020600c2091825442116106fe5750600073ffffffffffffffffffffffffffffffffffffffff9255167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a355005b636f5e8818600052601cfd5b61071336612096565b6fffffffffffffffffffffffffffffffff92919293848216836000526001602052604060002095856000526003870160205260406000206001880154156109665760ff600382015460801c1660058110156109385760010361090f5782156108e6576002610782338989612ec6565b9889600052016020526040600020937dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8554166108be57507e01000000000000000000000000000000000000000000000000000000000000929160016108589201908382547fffffffffffffffffffffffffffffffff000000000000000000000000000000008383818416011691161780845560801c01166fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b7fff000000000000000000000000000000000000000000000000000000000000008354161717905561088b348233612652565b60405193845260208401527f76be093b9f8b5c898a6038f68c84b4dc903bc2fbefdc455a35a86fbe0875acc860403394a4005b6040517f68cf1349000000000000000000000000000000000000000000000000000000008152fd5b836040517f3c4a9486000000000000000000000000000000000000000000000000000000008152fd5b836040517f0ea6e1e7000000000000000000000000000000000000000000000000000000008152fd5b6021857f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b836040517fb0cfa447000000000000000000000000000000000000000000000000000000008152fd5b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760207f6e4bcc82cb4b918df6b911c8092c49f027b7d409733e2c97bb0a4b5a367e19db91356109eb6121fd565b75ffffffffffffffffffffffffffffffffffffffffffff81167fffffffffffffffffffff000000000000000000000000000000000000000000006003541617600355604051908152a1005b610a3f366121a8565b83600095929395526001918260205260406000208660005260038101602052604060002092600384019560ff875460801c16600581101561093857600203610e545767ffffffffffffffff9182865460401c1691428311610e2b5787850154600a8401948511610dfd5790610ab694939291612895565b509184880190818911610dcf575091610b018783610afc8460a09997847fac40c95b92f792b83170dfdb011f9e519b63f5990b694950d73c48ca504dd52f9c9a98612d96565b612c4d565b600092600060028401906fffffffffffffffffffffffffffffffff9283835416151580610dc0575b15610d385780860184815416928560035494610bbd610b7c610b5c84610b6961ffff8b8361271095869260f01c8a612271565b16049b60e01c168b612271565b160497610b768982612298565b93612298565b88546fffffffffffffffffffffffffffffffff1660809190911b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000016178855565b1660025401600255875460c01d60070b8760070b818113600014610cf15750505091610c98868693610cc8955460801c7fffffffffffffffffffffffffffffffff000000000000000000000000000000008d5416178c55985b7003000000000000000000000000000000007fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff8d5416178c55907fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff82549160801b77ffffffffffffffff00000000000000000000000000000000169116179055565b168060005403600055337f0000000000000000000000000000000000000000000000000000000000000000612f26565b5460801c945416604051948552602085015260070b6040840152606083015215156080820152a3005b879450610cc8959350988891610c98939a1215610c1657848754167fffffffffffffffffffffffffffffffff000000000000000000000000000000008d5416178c55610c16565b87547fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff1670040000000000000000000000000000000017885585547fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1677ffffffffffffffff00000000000000000000000000000000608087901b1617909555939450610cc8565b508086015460801c1515610b29565b6011907f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b6011877f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b856040517fde8f5316000000000000000000000000000000000000000000000000000000008152fd5b836040517fc38434ab000000000000000000000000000000000000000000000000000000008152fd5b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576020600054604051908152f35b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857803561ffff8116918282036101e857610f6e6121fd565b6103e88311610ff3577f5aa89d2018da401c466211564e2994895ca1dc68df9d55db7482e437dadc0811602084847dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffff0000000000000000000000000000000000000000000000000000000000006003549260f01b16911617600355604051908152a1005b60249083604051917f35a30751000000000000000000000000000000000000000000000000000000008352820152fd5b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760206040516103e88152f35b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576020600254604051908152f35b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760206040516127108152f35b346101e85760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85761110c6120ce565b9060243573ffffffffffffffffffffffffffffffffffffffff91828216938483036101e85761114760443580946111416121fd565b83612f26565b836111518261300e565b9116937f000000000000000000000000000000000000000000000000000000000000000016841490816111da575b506111b2575060207f16a1412f01b73c390eb2548427101644aa86c1443c272f73df00fb74c48fe49991604051908152a3005b6040517ff4d678b8000000000000000000000000000000000000000000000000000000008152fd5b9050600054118561117f565b6111ef366121a8565b83600095929395526001602052604060002092856000526003840160205260406000209160038301805460ff8160801c1660058110156104f4576002036113425767ffffffffffffffff80955460401c166103848101809111610dfd574210610e2b577fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff70040000000000000000000000000000000091161790556001850154917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc4420193428511611314576112cd94428216949116929190612895565b50906001850190818611610dcf575091610afc84926112ed948484612d96565b7f4be96238c3feeb1d8156e058500c5d1f6d147bb1c234e2019998bfbabe2b4a5c600080a3005b6011867f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b856040517fc38434ab000000000000000000000000000000000000000000000000000000008152fd5b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760206040516103848152f35b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346101e85761146161143261142b36612146565b339161239e565b8060005403600055337f0000000000000000000000000000000000000000000000000000000000000000612f26565b005b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85760806003546040519075ffffffffffffffffffffffffffffffffffffffffffff8116825265ffffffffffff8160b01c16602083015261ffff8160e01c16604083015260f01c6060820152f35b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8573563ffffffff8116908181036101e8577f50e5c1c885ea2510512f889012b87e95d7696281a6c2985c6c7d45fda6d792d59160209161154a6121fd565b7fffffffff000000000000ffffffffffffffffffffffffffffffffffffffffffff79ffffffff000000000000000000000000000000000000000000006003549260b01b16911617600355604051908152a1005b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8573560005260016020526040806000206001815491015482519182526020820152f35b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576116206121fd565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739278181547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a355005b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85780356024359167ffffffffffffffff928381116101e8576116bb903690830161217a565b9390918360005260016020526040600020926001840195865461187f577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed4420192428411610dcf575061146196848761174381967fccd5d394b6c44121d18f7dc9af1472e400cd79e3318c578aca4eeacb5d9df7a4966040968560019b421693169185612895565b509255847fb695ede53322ecf2e50fbc58c9cd70baa3835819be7d0c523a69efcab3c44bf560208551338152a261177a8588612c4d565b856000526003870160205282600020906117a165ffffffffffff60035460b01c1642612235565b1690600381017002000000000000000000000000000000007fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff8254161790556118268282907fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff000000000000000083549260401b169116179055565b805477ffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffff0000000000000000000000000000000000000000000000008560c01b16911617905582519160070b82526020820152a3612c4d565b826040517fde3c363f000000000000000000000000000000000000000000000000000000008152fd5b346101e8576118b636612146565b908060005260016020526040600020928260005260038401602052604060002060026118e3338686612ec6565b95866000520160205260ff6003604060002092015460801c166005811015611a37578203611a0e578054918260f81c6119e657507f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83161781556119b17dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8093168060005403600055337f0000000000000000000000000000000000000000000000000000000000000000612f26565b541660405193845260208401527ffb75c09f4826c289f841e35b10c86b0e385d3f01ff49cfc12d5626d24645f64a60403394a4005b6040517f4f9e331b000000000000000000000000000000000000000000000000000000008152fd5b506040517fc38434ab000000000000000000000000000000000000000000000000000000008152fd5b6021837f4e487b71000000000000000000000000000000000000000000000000000000006000525260246000fd5b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85763389a75e1600c523360005260006020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92600080a2005b346101e8576002611aff611ade366120f1565b90611aea93929361237f565b50836000526001602052604060002093612ec6565b600052016020526060604060002060405190611b1a826122ba565b547dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116825260ff8160f01c16602083015260f81c151560408201526104c46040518092604080917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff815116845260ff602082015116602085015201511515910152565b346101e85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857611bd16120ce565b90602435611bdd6121fd565b80611c785750600254905b60025490828203918211610dcf575060025580600054036000557f000000000000000000000000000000000000000000000000000000000000000090611c2f818484612f26565b6040519081527f4faa6fbacd77a523e80ea39326e5d792fb633131f897c119ec498d60f4bd68f9602073ffffffffffffffffffffffffffffffffffffffff8095169433941692a4005b90611be8565b346101e85760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e8576020604051600a8152f35b346101e85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857602480359167ffffffffffffffff908035908285116101e857366023860112156101e8578401359182116101e857600590368484841b870101116101e8579291906000936000935b838510611d6957611461868060005403600055337f0000000000000000000000000000000000000000000000000000000000000000612f26565b9091929394506001611d83338388871b8a0101358561239e565b950193929190611d2f565b60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e85763389a75e1600c52336000526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d600080a2005b346101e85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101e857803561ffff8116918282036101e857611e3e6121fd565b6103e88311610ff3577f5aa89d2018da401c466211564e2994895ca1dc68df9d55db7482e437dadc0811602084847fffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff7dffff000000000000000000000000000000000000000000000000000000006003549260e01b16911617600355604051908152a1005b346101e857611461611ee1611ed7366120f1565b929190839161239e565b9081600054036000557f0000000000000000000000000000000000000000000000000000000000000000612f26565b90611f1a36612096565b90926fffffffffffffffffffffffffffffffff9485831690846000526001602052604060002096866000526003880160205260406000209160018901541561206f575060ff600383015460801c1660058110156109385760010361090f5782156108e6576002611f8b338989612ec6565b9889600052016020526040600020937dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8554166108be5750600260018301928354937fffffffffffffffffffffffffffffffff000000000000000000000000000000009485858881841601169116179055019182549184818416011691161790557fff0000000000000000000000000000000000000000000000000000000000000082541617905561203c348233612652565b60405193845260208401527facad006dfb05c480e68abb20c66f13c27f5971dd27fb0e3fa9e4f0f6d1a3abee60403394a4005b807fb0cfa44700000000000000000000000000000000000000000000000000000000869252fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101e857600435906024359060443590565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101e857565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101e857600435906024359060443573ffffffffffffffffffffffffffffffffffffffff811681036101e85790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60409101126101e8576004359060243590565b9181601f840112156101e85782359167ffffffffffffffff83116101e857602083818601950101116101e857565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126101e85760043591602435916044359067ffffffffffffffff82116101e8576121f99160040161217a565b9091565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754330361222757565b6382b429006000526004601cfd5b9190820180921161224257565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9190916fffffffffffffffffffffffffffffffff8080941691160291821691820361224257565b6fffffffffffffffffffffffffffffffff918216908216039190821161224257565b6060810190811067ffffffffffffffff8211176122d657604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff8211176122d657604052565b6040810190811067ffffffffffffffff8211176122d657604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176122d657604052565b6040519061238c826122ba565b60006040838281528260208201520152565b909291926000918083526001602052604080842093838152600385016020528181209060026123ce898787612ec6565b9687835201602052828120916003810160ff815460801c166005811015612625576003036125fc578354917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff948584169384156125d3578060f81c6125aa577effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0100000000000000000000000000000000000000000000000000000000000000179081905560f01c60ff166001036125945780548060c01d60070b9060801c60070b135b1561256b576002015460801c84818402169283040361253e57546fffffffffffffffffffffffffffffffff16918215612511575083519687520416602085018190529573ffffffffffffffffffffffffffffffffffffffff16937f31295a2f764b13b07abb49b6fd4fe8a7ca220b09c72570d04cd947871c999d1a9190a4565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526012600452fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b600486517f4f97cab6000000000000000000000000000000000000000000000000000000008152fd5b80548060c01d60070b9060801c60070b12612491565b600488517f4f9e331b000000000000000000000000000000000000000000000000000000008152fd5b600488517f6ec9be11000000000000000000000000000000000000000000000000000000008152fd5b600485517fc38434ab000000000000000000000000000000000000000000000000000000008152fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6000805483018155907f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff82166126cc57505050036126a257565b60046040517f025dbdd4000000000000000000000000000000000000000000000000000000008152fd5b82939450601c8360209493606493604051986060523060405260601b602c526f23b872dd000000000000000000000000600c525af13d156001835114171690606052816040521561271a5750565b807fa512d51e0000000000000000000000000000000000000000000000000000000060049252fd5b80511561274f5760200190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9080825180825260208092019180808360051b86010195019360009384915b8483106127ae575050505050505090565b90919293949596847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08084840301855289518051908185528a5b82811061281457505080840183018a9052601f011690910181019781019695949360010192019061279d565b8181018501518682018601528994016127e8565b91908260809103126101e85760405167ffffffffffffffff9260808201848111838210176122d657604052819381518060070b81036101e8578352602082015190811681036101e857602083015260408101518060030b81036101e8576040830152606090810151910152565b92939190916040938451906128a982612322565b60019283835260209788840192893685376128c385612742565b528751966128d088612322565b8588528960005b818110612c3d57505067ffffffffffffffff968781116122d6578951916129258c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116018461233e565b81835236828201116101e857816000928d92838601378301015261294888612742565b5261295287612742565b5073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002880ab155794e7179c9ee2e38200202908c17b4316948851947fd47eed450000000000000000000000000000000000000000000000000000000086528a60048701528a86806129c7602482018d61277e565b03818a5afa958615612c3257600096612c00575b50853410612bd757918792612a2899969594928c8c519b8c997f4716e9c5000000000000000000000000000000000000000000000000000000008b52608060048c015260848b019061277e565b8981037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0160248b015292518084529201959160008e5b828210612bbd5750505050908316604487015250166064840152600093908390039183915af1918215612bb257600092612ab1575b5050612aa09150612742565b510151906060825160070b92015190565b90913d8082843e612ac2818461233e565b8201918581840312612b7d57805190848211612bae57019180601f84011215612b7d578251938411612b8157845194612b00878660051b018761233e565b848652868601928761012080970286010194838611612b7d578801935b858510612b365750505050505050612aa0903880612a94565b8685850312612b7d578887918451612b4d816122ba565b87518152612b5d87848a01612828565b83820152612b6e8760a08a01612828565b86820152815201940193612b1d565b5080fd5b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8280fd5b83513d6000823e3d90fd5b845189528d9a50978801978c97509093019282018e612a5f565b60048a517f025dbdd4000000000000000000000000000000000000000000000000000000008152fd5b90958b82813d8311612c2b575b612c17818361233e565b81010312612c2857505194386129db565b80fd5b503d612c0d565b8a513d6000823e3d90fd5b606082828c010152018a906128d7565b9081547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114612242576001019182815567ffffffffffffffff9081421660035460af1c6601fffffffffffe65fffffffffffe82169116810361224257810192808411612242576000868152600393840160205260409081902093840180547fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000017905583547fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff84167fffffffffffffffffffffffffffffffff000000000000000000000000000000009091161785821b6fffffffffffffffff000000000000000016179093557f30745dda6a7b321538e5fc06390354a4f93646c86e3d8d29dad0b0beeff58098938351928352166020820152a3565b7fccd5d394b6c44121d18f7dc9af1472e400cd79e3318c578aca4eeacb5d9df7a4919293600360409286600052016020528160002067ffffffffffffffff612deb65ffffffffffff60035460b01c1642612235565b1690600381017002000000000000000000000000000000007fffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffff825416179055612e708282907fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff000000000000000083549260401b169116179055565b805477ffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffff0000000000000000000000000000000000000000000000008560c01b16911617905582519160070b82526020820152a3565b917fffffffffffffffffffffffffffffffffffffffff000000000000000000000000906040519260208401948552604084015260601b166060820152605481526080810181811067ffffffffffffffff8211176122d65760405251902090565b73ffffffffffffffffffffffffffffffffffffffff90808216612f7f575050600080809381935af115612f5557565b60046040517ff4b3b1bc000000000000000000000000000000000000000000000000000000008152fd5b6020906040929483519586957fa9059cbb000000000000000000000000000000000000000000000000000000008752166004860152602485015260446000948580935af13d15601f3d11600185511416171692828152826020820152015215612fe457565b60046040517ff27f64e4000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff168061302e57504790565b6020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa9081156130a557600091613077575090565b906020823d821161309d575b816130906020938361233e565b81010312612c2857505190565b3d9150613083565b6040513d6000823e3d90fdfea2646970667358221220c8b84f57112d19e1a7c44a61808e583205ee5d1dc30194a4ae7e43c7ff74620d64736f6c63430008140033