Ape Curtis Testnet

Contract

0x93101fd935337E2e427168088369e104c7f27d4B

Overview

APE Balance

Ape Curtis LogoApe Curtis LogoApe Curtis Logo0 APE

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

9 Internal Transactions found.

Latest 9 internal transactions

Parent Transaction Hash Block From To
142046912024-12-27 12:03:4143 hrs ago1735301021
0x93101fd9...4c7f27d4B
0 APE
142046842024-12-27 12:03:2143 hrs ago1735301001
0x93101fd9...4c7f27d4B
0 APE
142027912024-12-27 9:40:4346 hrs ago1735292443
0x93101fd9...4c7f27d4B
0 APE
142026722024-12-27 9:31:5046 hrs ago1735291910
0x93101fd9...4c7f27d4B
0 APE
142026532024-12-27 9:30:1946 hrs ago1735291819
0x93101fd9...4c7f27d4B
0 APE
142026412024-12-27 9:29:1846 hrs ago1735291758
0x93101fd9...4c7f27d4B
0 APE
141787382024-12-25 23:17:263 days ago1735168646
0x93101fd9...4c7f27d4B
0 APE
141786352024-12-25 22:56:443 days ago1735167404
0x93101fd9...4c7f27d4B
0 APE
141621412024-12-24 23:42:114 days ago1735083731
0x93101fd9...4c7f27d4B
0 APE

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PredictionMarket

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 2000 runs

Other Settings:
paris EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 15 : PredictionMarket.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "./interfaces/IPredictionMarket.sol";
import "./interfaces/IMarketFactory.sol";
import "./libraries/Constants.sol";

/**
 * @title PredictionMarket
 * @dev Implementation of a prediction market that allows users to buy shares
 * and participate in events based on their outcomes. The contract supports multiple
 * events, each with its own status, and enforces permissioned access for oracles.
 * The market ensures safe token handling using OpenZeppelin's SafeERC20.
 */
contract PredictionMarket is IPredictionMarket, Initializable {
    using SafeERC20 for IERC20;

    /** @notice Address of the market factory contract that deployed this market. */
    address public override factory;

    /** @notice Address of the ERC20 token used for deposits in the market. */
    address public override token;

    /** @notice Base price for buying shares in the market. */
    uint256 public override basePrice;

    /** @notice Fee percentage for buying "no" shares in the market. */
    uint256 public override noBuyFeePercentage;

    /** @notice Fee percentage applied when claiming "yes" outcomes. */
    uint256 public override yesClaimFeePercentage;

    /** @notice Duration (in seconds) for which an event remains active. */
    uint256 public override eventDuration;

    /** @notice Mapping of events to their data, including status, balances, and deposits. */
    mapping(PredictionMarketEvent => EventData) public events;

    /** @notice Currently active event in the market. */
    PredictionMarketEvent internal _activeEvent;

    /**
     * @dev Modifier to ensure the market is not paused.
     */
    modifier whenNotPaused() {
        if (IMarketFactory(factory).paused()) {
            revert EnforcedPause();
        }
        _;
    }

    /**
     * @dev Modifier to restrict access to authorized oracles only.
     */
    modifier onlyOracle() {
        if (!IMarketFactory(factory).isOracle(msg.sender)) {
            revert UnauthorizedOracle(msg.sender);
        }
        _;
    }
    /**
     * @dev Modifier to restrict access to authorized oracles only.
     */
    modifier onlyTresuary() {
        if (!(IMarketFactory(factory).tresuary() == msg.sender)) {
            revert AccessDenied();
        }
        _;
    }

    /**
     * @dev Disables initializers to prevent misuse during deployment.
     */
    constructor() {
        _disableInitializers();
    }

    /**
     * @notice Initializes the market with the provided parameters.
     * @param params_ Struct containing the initial configuration of the market.
     */
    function initialize(
        PredictionMarketInitialParams calldata params_
    ) external initializer {
        factory = msg.sender;
        token = params_.token;
        basePrice = params_.basePrice;
        noBuyFeePercentage = params_.noBuyFeePercentage;
        yesClaimFeePercentage = params_.yesClaimFeePercentage;
        eventDuration = params_.eventDuration;

        for (
            uint8 i = uint8(params_.initialEvent);
            i <= uint8(PredictionMarketEvent.REACH_5_000_000_CAP);
            i++
        ) {
            _updateEventStatus(PredictionMarketEvent(i), EventStatus.AVAILABLE);
        }
        _updateActiveEvent(params_.initialEvent);
    }

    /**
     * @notice Starts the waiting result state for a specific event.
     * @param event_ The event to transition to waiting result state.
     *
     * Requirements:
     * - The caller must be an authorized oracle.
     * - The event must be the currently active event.
     * - The event's status must be `AVAILABLE`.
     */
    function startWaitingResultState(
        PredictionMarketEvent event_
    ) external onlyOracle {
        if (_activeEvent != event_) {
            revert InvalidActiveEvent(event_);
        }
        if (events[event_].status != EventStatus.AVAILABLE) {
            revert InvalidEventStatus();
        }
        events[event_].startWaitingResultBlock = block.number;

        _updateEventStatus(event_, EventStatus.WAITING_RESULT);
    }

    /**
     * @notice Processes the result of an event and updates its status accordingly.
     * @param event_ The event to process.
     * @param result_ The result of the event (`true` for "yes", `false` for "no").
     *
     * Requirements:
     * - The caller must be an authorized oracle.
     * - The event must be the currently active event.
     * - The event's status must be `WAITING_RESULT`.
     * - The result must be processed after the waiting block.
     */
    function proccessEventResult(
        PredictionMarketEvent event_,
        bool result_
    ) external onlyOracle {
        if (_activeEvent != event_) {
            revert InvalidActiveEvent(event_);
        }

        if (events[event_].status != EventStatus.WAITING_RESULT) {
            revert InvalidEventStatus();
        }

        if (events[event_].startWaitingResultBlock >= block.number) {
            revert InvalidBlockSkip();
        }
        EventStatus newStatus = result_
            ? EventStatus.RESULT_YES
            : EventStatus.RESULT_NO;

        if (newStatus == EventStatus.RESULT_YES) {
            _updateEventStatus(event_, newStatus);
            if (event_ != PredictionMarketEvent.REACH_5_000_000_CAP) {
                _updateActiveEvent(PredictionMarketEvent(uint8(event_) + 1));
            }
        } else {
            for (
                uint8 i = uint8(event_);
                i <= (uint8(PredictionMarketEvent.REACH_5_000_000_CAP));
                i++
            ) {
                _updateEventStatus(PredictionMarketEvent(i), newStatus);
            }
        }
    }

    /**
     * @notice Purchases "yes" shares for a specific event.
     * @param event_ The event for which shares are being purchased.
     * @param sharesAmount_ The number of shares to purchase.
     * @return depositAmount The amount of tokens deposited for the purchase.
     *
     * Emits a {BuyYesShares} event.
     */
    function buyYesShares(
        PredictionMarketEvent event_,
        uint256 sharesAmount_
    ) external whenNotPaused returns (uint256 depositAmount) {
        _checkAvailableEvent(event_);

        depositAmount = calculateYesDepositAmountIn(sharesAmount_);

        _depositToken().safeTransferFrom(
            msg.sender,
            address(this),
            depositAmount
        );

        events[event_].balances[msg.sender].yesShares += sharesAmount_;
        events[event_].balances[msg.sender].yesDeposit += depositAmount;
        events[event_].yesDepositTotal += depositAmount;
        events[event_].yesSharesTotal += sharesAmount_;

        emit BuyYesShares(msg.sender, event_, depositAmount, sharesAmount_, 0);
    }

    /**
     * @notice Purchases "no" shares for a specific event.
     * @param event_ The event for which shares are being purchased.
     * @param sharesAmount_ The number of shares to purchase.
     * @return depositAmount The total amount of tokens deposited, including fees.
     *
     * Requirements:
     * - The event must be available.
     * - The market must not be paused.
     *
     * Emits a {BuyNoShares} event.
     */
    function buyNoShares(
        PredictionMarketEvent event_,
        uint256 sharesAmount_
    ) external whenNotPaused returns (uint256 depositAmount, uint256 fee) {
        _checkAvailableEvent(event_);

        uint256 depositAmountWithoutFee;
        (
            depositAmount,
            depositAmountWithoutFee
        ) = calculateNoSharesDepositAmountIn(sharesAmount_);

        _depositToken().safeTransferFrom(
            msg.sender,
            address(this),
            depositAmount
        );

        fee = depositAmount - depositAmountWithoutFee;

        events[event_].balances[msg.sender].noShares += sharesAmount_;
        events[event_]
            .balances[msg.sender]
            .noDepoosit += depositAmountWithoutFee;
        events[event_].noDepositTotal += depositAmountWithoutFee;
        events[event_].noSharesTotal += sharesAmount_;
        events[event_].feeAccumulated += fee;

        emit BuyNoShares(
            msg.sender,
            event_,
            depositAmountWithoutFee,
            sharesAmount_,
            fee
        );
    }

    /**
     * @notice Withdraws accumulated fees for a specific event.
     * @param event_ The event for which fees are being withdrawn.
     *
     * Requirements:
     * - Caller must be the treasury.
     * - The event must have a "RESULT_NO" or "RESULT_YES" status.
     * - The accumulated fee must be greater than 0.
     *
     * Emits a {WithdrawFee} event.
     */
    function withdrawFee(PredictionMarketEvent event_) external onlyTresuary {
        EventStatus eventStatus = events[event_].status;
        if (
            eventStatus != EventStatus.RESULT_NO &&
            eventStatus != EventStatus.RESULT_YES
        ) {
            revert InvalidEventStatus();
        }

        uint256 fee = events[event_].feeAccumulated;
        if (fee == 0) {
            revert ZeroAmountToClaim();
        }

        events[event_].feeAccumulated = 0;

        _depositToken().safeTransfer(msg.sender, fee);

        emit WithdrawFee(msg.sender, event_, fee);
    }

    /**
     * @notice Claims rewards for a specific event based on the user's shares.
     * @param event_ The event for which rewards are being claimed.
     *
     * Requirements:
     * - The event must have a "RESULT_NO" or "RESULT_YES" status.
     * - The user must have an unclaimed balance.
     *
     * Emits a {Claim} event.
     */
    function claim(PredictionMarketEvent event_) external whenNotPaused {
        EventStatus eventStatus = events[event_].status;
        if (
            eventStatus != EventStatus.RESULT_NO &&
            eventStatus != EventStatus.RESULT_YES
        ) {
            revert InvalidEventStatus();
        }

        (uint256 amount, uint256 fee) = getAvailableToClaim(msg.sender, event_);
        if (amount == 0) {
            revert ZeroAmountToClaim();
        }
        events[event_].balances[msg.sender].claimed += amount;

        if (fee > 0) {
            events[event_].feeAccumulated += fee;
        }

        _depositToken().safeTransfer(msg.sender, amount);

        emit Claim(msg.sender, event_, amount, fee);
    }

    /**
     * @notice Retrieves the amount available to claim for a user for a specific event.
     * @param user_ The address of the user.
     * @param event_ The event for which the claimable amount is being calculated.
     * @return amountOut The total amount available to claim for the user.
     * @return fee The fee applicable on the claim (if any).
     *
     * Requirements:
     * - The user must not have already claimed for the event.
     * - The event must have a "RESULT_NO" or "RESULT_YES" status.
     *
     * Logic:
     * - If the event result is "NO", calculates the user's proportionate share from total deposits.
     * - If the event result is "YES", deducts a claim fee from the user's share of total deposits.
     */
    function getAvailableToClaim(
        address user_,
        PredictionMarketEvent event_
    ) public view returns (uint256 amountOut, uint256 fee) {
        EventStatus eventStatus = events[event_].status;
        if (events[event_].balances[msg.sender].claimed > 0) {
            return (0, 0);
        }

        uint256 totalDeposits = events[event_].yesDepositTotal +
            events[event_].noDepositTotal;
        if (totalDeposits == 0) {
            return (0, 0);
        }
        if (eventStatus == EventStatus.RESULT_NO) {
            amountOut =
                (events[event_].balances[user_].noShares * totalDeposits) /
                events[event_].noSharesTotal;
        } else if (eventStatus == EventStatus.RESULT_YES) {
            amountOut =
                (events[event_].balances[user_].yesShares * totalDeposits) /
                events[event_].yesSharesTotal;

            fee = (amountOut * yesClaimFeePercentage) / PRECISION;
            amountOut -= fee;
        }
    }

    /**
     * @notice Retrieves the balance details of a user for a specific event.
     * @param user_ The address of the user.
     * @param event_ The event for which balances are being queried.
     * @return The user's balance details as a `UserBalance` struct.
     */
    function getUserBalances(
        address user_,
        PredictionMarketEvent event_
    ) external view returns (UserBalance memory) {
        return events[event_].balances[user_];
    }

    /**
     * @notice Returns the currently active event in the market.
     * @return The active event as a `PredictionMarketEvent`.
     */
    function getActiveEvent() public view returns (PredictionMarketEvent) {
        return (_activeEvent);
    }

    /**
     * @notice Calculates the deposit amount required to buy "no" shares, including fees.
     * @param sharesAmount_ The number of "no" shares to purchase.
     * @return depositAmountInWithFee The total deposit amount including fees.
     * @return depositAmountIn The deposit amount excluding fees.
     */
    function calculateNoSharesDepositAmountIn(
        uint256 sharesAmount_
    )
        public
        view
        returns (uint256 depositAmountInWithFee, uint256 depositAmountIn)
    {
        uint256 priceWithFee = basePrice +
            (basePrice * noBuyFeePercentage) /
            1e18;
        depositAmountInWithFee = (sharesAmount_ * priceWithFee) / 1e18;
        depositAmountIn = (sharesAmount_ * basePrice) / 1e18;
    }

    /**
     * @notice Calculates the deposit amount required to buy "yes" shares.
     * @param sharesAmount_ The number of "yes" shares to purchase.
     * @return The required deposit amount.
     */
    function calculateYesDepositAmountIn(
        uint256 sharesAmount_
    ) public view returns (uint256) {
        return (sharesAmount_ * basePrice) / 1e18;
    }

    /**
     * @notice Checks if the current active event is in the waiting result state.
     * @return True if the active event is waiting for a result, otherwise false.
     */
    function isWaitingResult() public view returns (bool) {
        PredictionMarketEvent activeEventCache = _activeEvent;
        EventStatus eventStatus = events[activeEventCache].status;

        if (eventStatus == EventStatus.WAITING_RESULT) {
            return true;
        }

        return
            eventStatus == EventStatus.AVAILABLE &&
            block.timestamp >= events[activeEventCache].endTime;
    }

    /**
     * @notice Updates the currently active event and sets its end time.
     * @param marketEvent_ The event to set as active.
     *
     * Emits a {NextActiveEvent} event.
     */
    function _updateActiveEvent(PredictionMarketEvent marketEvent_) internal {
        _activeEvent = marketEvent_;
        uint256 endTime = block.timestamp + eventDuration;
        events[marketEvent_].endTime = endTime;
        emit NextActiveEvent(msg.sender, marketEvent_, endTime);
    }

    /**
     * @notice Updates the status of a specific event.
     * @param marketEvent_ The event to update.
     * @param status_ The new status to set for the event.
     *
     * Emits an {UpdateEventStatus} event.
     */
    function _updateEventStatus(
        PredictionMarketEvent marketEvent_,
        EventStatus status_
    ) internal {
        events[marketEvent_].status = status_;
        emit UpdateEventStatus(msg.sender, marketEvent_, status_);
    }

    /**
     * @notice Validates that an event is available for participation.
     * @param event_ The event to validate.
     *
     * Requirements:
     * - The market must not be waiting for a result.
     * - The event's status must be `AVAILABLE`.
     */
    function _checkAvailableEvent(PredictionMarketEvent event_) internal view {
        if (isWaitingResult()) {
            revert WaitingResult();
        }

        if (events[event_].status != EventStatus.AVAILABLE) {
            revert EventNotAvailable();
        }
    }

    /**
     * @notice Retrieves the deposit token used by the market.
     * @return The deposit token as an `IERC20` interface.
     */
    function _depositToken() internal view returns (IERC20) {
        return IERC20(IMarketFactory(factory).depositToken());
    }
}

File 2 of 15 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

File 3 of 15 : IERC1363.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

File 4 of 15 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

File 5 of 15 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

File 6 of 15 : IBeacon.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {UpgradeableBeacon} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

File 7 of 15 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

File 8 of 15 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

File 9 of 15 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

File 10 of 15 : Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}

File 11 of 15 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

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

File 12 of 15 : IMarketFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

import "@openzeppelin/contracts/proxy/beacon/IBeacon.sol";
import "../libraries/MarketStruct.sol";

/**
 * @title IMarketFactory
 * @dev Interface for the MarketFactory contract.
 */
interface IMarketFactory is IBeacon {
    /**
     * @notice Emitted when the treasury address is updated.
     * @param tresuary The new treasury address.
     */
    event Tresuary(address indexed tresuary);

    /**
     * @notice Emitted when the base price is updated.
     * @param basePrice The new base price.
     */
    event BasePrice(uint256 indexed basePrice);

    /**
     * @notice Emitted when the event duration is updated.
     * @param duration The new event duration.
     */
    event EventDuration(uint256 indexed duration);

    /**
     * @notice Emitted when the "no buy" fee percentage is updated.
     * @param noBuyFeePercentage The new "no buy" fee percentage.
     */
    event NoBuyFeePercentage(uint256 indexed noBuyFeePercentage);

    /**
     * @notice Emitted when the "yes claim" fee percentage is updated.
     * @param yesClaimFeePercentage The new "yes claim" fee percentage.
     */
    event YesClaimFeePercentage(uint256 indexed yesClaimFeePercentage);

    /**
     * @notice Emitted when a new market is created.
     * @param market The address of the created market.
     * @param signer The signer who authorized the market creation.
     * @param token The token address associated with the market.
     * @param creator The creator of the market.
     * @param basePrice The base price for the market.
     * @param noBuyFeePercentage The "no buy" fee percentage.
     * @param yesClaimFeePercentage The "yes claim" fee percentage.
     * @param initialEvent The initial event type of the market.
     * @param eventDuration The duration of the event.
     */
    event MarketCreated(
        address indexed market,
        address indexed signer,
        address indexed token,
        address creator,
        uint256 basePrice,
        uint256 noBuyFeePercentage,
        uint256 yesClaimFeePercentage,
        PredictionMarketEvent initialEvent,
        uint256 eventDuration
    );

    /**
     * @dev Error thrown when the market creation signature is expired.
     * @param deadline The deadline that has passed.
     */
    error CreateMarketSignatureExpired(uint256 deadline);

    /**
     * @dev Error thrown when the signer for market creation is invalid.
     * @param signer The invalid signer address.
     * @param from The address attempting to create the market.
     */
    error CreateMarketInvalidSigner(address signer, address from);

    /**
     * @dev Error thrown when a fee percentage exceeds the maximum allowed value.
     * @param feePercentage The fee percentage that exceeded the limit.
     */
    error GtThenMaxFeePercentage(uint256 feePercentage);

    /**
     * @dev Error thrown when a market already exists for a specific token.
     * @param token The token address for which the market exists.
     */
    error MarketAlreadyExist(address token);

    /**
     * @notice Creates a new prediction market.
     * @param params_ The parameters required to create the market.
     * @return The address of the newly created market.
     */
    function createMarket(
        PredictionMarketCreateParams calldata params_
    ) external returns (address);

    /**
     * @notice Updates the treasury address.
     * @param tresuary_ The new treasury address.
     */
    function setTresuary(address tresuary_) external;

    /**
     * @notice Updates the base price for market creation.
     * @param basePrice_ The new base price.
     */
    function setBasePrice(uint256 basePrice_) external;

    /**
     * @notice Updates the "no buy" fee percentage.
     * @param noBuyFeePercentage_ The new "no buy" fee percentage.
     */
    function setNoBuyFeePercentage(uint256 noBuyFeePercentage_) external;

    /**
     * @notice Updates the "yes claim" fee percentage.
     * @param yesClaimFeePercentage_ The new "yes claim" fee percentage.
     */
    function setYesClaimFeePercentage(uint256 yesClaimFeePercentage_) external;

    /**
     * @notice Updates the event duration for markets.
     * @param eventDuration_ The new event duration.
     */
    function setEventDuration(uint256 eventDuration_) external;

    /**
     * @notice Pauses or unpauses the contract.
     * @param pause_ True to pause, false to unpause.
     */
    function setPause(bool pause_) external;

    /**
     * @notice Checks if the specified address has the `ORACLE_ROLE`.
     * @param oracle_ The address to check.
     * @return True if the address has the `ORACLE_ROLE`, otherwise false.
     */
    function isOracle(address oracle_) external view returns (bool);

    /**
     * @notice Checks if the contract is paused.
     * @return True if the contract is paused, otherwise false.
     */
    function paused() external view returns (bool);

    /**
     * @notice Returns the deposit token address.
     * @return The deposit token address.
     */
    function depositToken() external view returns (address);

    /**
     * @notice Returns the implementation address.
     * @return The implementation address.
     */
    function implementation() external view returns (address);

    /**
     * @notice Returns the treasury address.
     * @return The treasury address.
     */
    function tresuary() external view returns (address);

    /**
     * @notice Returns the base price for market creation.
     * @return The base price.
     */
    function basePrice() external view returns (uint256);

    /**
     * @notice Returns the event duration for markets.
     * @return The event duration.
     */
    function eventDuration() external view returns (uint256);

    /**
     * @notice Returns the "no buy" fee percentage.
     * @return The "no buy" fee percentage.
     */
    function noBuyFeePercentage() external view returns (uint256);

    /**
     * @notice Returns the "yes claim" fee percentage.
     * @return The "yes claim" fee percentage.
     */
    function yesClaimFeePercentage() external view returns (uint256);

    /**
     * @notice Returns the length of the markets array.
     * @return The number of markets.
     */
    function marketsLength() external view returns (uint256);

    /**
     * @notice Returns the market address associated with a specific index.
     * @return The market address.
     */
    function markets(uint256 index_) external view returns (address);

    /**
     * @notice Returns a list of market addresses within the specified range.
     * @param offset_ The starting index of the markets array.
     * @param limit_ The maximum number of markets to return.
     * @return A list of market addresses.
     */
    function getMarkets(
        uint256 offset_,
        uint256 limit_
    ) external view returns (address[] memory);

    /**
     * @notice Returns the market address associated with a specific token.
     * @param token The token address.
     * @return The market address.
     */
    function getMarketByToken(address token) external view returns (address);
}

File 13 of 15 : IPredictionMarket.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

import "../libraries/MarketStruct.sol";

/**
 * @title IPredictionMarket
 * @dev Interface for a prediction market contract allowing users to participate in events and buy shares.
 */
interface IPredictionMarket {
    /**
     * @dev Enum representing the status of an event.
     */
    enum EventStatus {
        COMPLETED_IN_PAST, // Event has already completed in the past
        AVAILABLE, // Event is currently available for participation
        WAITING_RESULT, // Event is waiting for a result to be provided
        RESULT_YES, // Event result is "YES"
        RESULT_NO // Event result is "NO"
    }

    /**
     * @dev Struct representing a user's balances for a specific event.
     */
    struct UserBalance {
        uint256 noDepoosit; // Total "no" deposits by the user
        uint256 yesDeposit; // Total "yes" deposits by the user
        uint256 noShares; // Total "no" shares owned by the user
        uint256 yesShares; // Total "yes" shares owned by the user
        uint256 claimed; // Total amount claimed by the user
    }

    /**
     * @dev Struct containing data for a specific event.
     */
    struct EventData {
        EventStatus status; // Current status of the event
        uint256 endTime; // Timestamp when the event ends
        uint256 startWaitingResultBlock; // Block number when waiting for result starts
        uint256 yesSharesTotal; // Total "yes" shares in the event
        uint256 noSharesTotal; // Total "no" shares in the event
        uint256 yesDepositTotal; // Total "yes" deposits in the event
        uint256 noDepositTotal; // Total "no" deposits in the event
        uint256 feeAccumulated; // Total fees accumulated for the event
        mapping(address => UserBalance) balances; // Mapping of user balances
    }

    /**
     * @dev Emitted when an event status is updated.
     * @param caller Address of the function caller
     * @param marketEvent The event being updated
     * @param status The new status of the event
     */
    event UpdateEventStatus(
        address indexed caller,
        PredictionMarketEvent indexed marketEvent,
        EventStatus indexed status
    );

    /**
     * @dev Emitted when a new active event is set.
     * @param caller Address of the function caller
     * @param marketEvent The new active event
     * @param endTime The end time of the new active event
     */
    event NextActiveEvent(
        address indexed caller,
        PredictionMarketEvent indexed marketEvent,
        uint256 endTime
    );

    /**
     * @dev Emitted when "yes" shares are purchased.
     * @param caller Address of the buyer
     * @param marketEvent The event for which shares are purchased
     * @param depositAmount Amount deposited for the shares
     * @param sharesAmountOut Number of shares purchased
     * @param fee Fee applied to the purchase
     */
    event BuyYesShares(
        address indexed caller,
        PredictionMarketEvent indexed marketEvent,
        uint256 depositAmount,
        uint256 sharesAmountOut,
        uint256 fee
    );

    /**
     * @dev Emitted when "no" shares are purchased.
     * @param caller Address of the buyer
     * @param marketEvent The event for which shares are purchased
     * @param depositAmount Amount deposited for the shares
     * @param sharesAmountOut Number of shares purchased
     * @param sharesFee Fee applied to the purchase
     */
    event BuyNoShares(
        address indexed caller,
        PredictionMarketEvent indexed marketEvent,
        uint256 depositAmount,
        uint256 sharesAmountOut,
        uint256 sharesFee
    );

    /**
     * @dev Emitted when a user claims their rewards.
     * @param caller Address of the claimant
     * @param marketEvent The event for which rewards are claimed
     * @param amount Amount claimed
     * @param fee Fee deducted from the claim
     */
    event Claim(
        address indexed caller,
        PredictionMarketEvent indexed marketEvent,
        uint256 amount,
        uint256 fee
    );

    /**
     * @dev Emitted when fees are withdrawn.
     * @param caller Address of the fee withdrawer (treasury)
     * @param marketEvent The event for which fees are withdrawn
     * @param fee Amount of fees withdrawn
     */
    event WithdrawFee(
        address indexed caller,
        PredictionMarketEvent indexed marketEvent,
        uint256 fee
    );

    /**
     * @dev Error thrown when the market is paused.
     */
    error EnforcedPause();

    /**
     * @dev Error thrown when an invalid active event is provided.
     * @param marketEvent The invalid event
     */
    error InvalidActiveEvent(PredictionMarketEvent marketEvent);

    /**
     * @dev Error thrown when an event has an invalid status.
     */
    error InvalidEventStatus();

    /**
     * @dev Error thrown when an invalid block is skipped.
     */
    error InvalidBlockSkip();

    /**
     * @dev Error thrown when an unauthorized oracle attempts an action.
     * @param caller Address of the unauthorized oracle
     */
    error UnauthorizedOracle(address caller);

    /**
     * @dev Error thrown when an event is not available for participation.
     */
    error EventNotAvailable();

    /**
     * @dev Error thrown when the market is waiting for a result.
     */
    error WaitingResult();

    /**
     * @dev Error thrown when attempting to claim a zero amount.
     */
    error ZeroAmountToClaim();

    /**
     * @dev Error thrown when access is denied to a caller.
     */
    error AccessDenied();

    /**
     * @notice Returns the factory address that deployed the market.
     */
    function factory() external view returns (address);

    /**
     * @notice Returns the address of the ERC20 token used for deposits.
     */
    function token() external view returns (address);

    /**
     * @notice Returns the base price for buying shares.
     */
    function basePrice() external view returns (uint256);

    /**
     * @notice Returns the fee percentage for buying "no" shares.
     */
    function noBuyFeePercentage() external view returns (uint256);

    /**
     * @notice Returns the fee percentage for claiming "yes" outcomes.
     */
    function yesClaimFeePercentage() external view returns (uint256);

    /**
     * @notice Returns the duration (in seconds) for which an event remains active.
     */
    function eventDuration() external view returns (uint256);

    /**
     * @notice Initializes the market with the provided parameters.
     * @param params_ Initial configuration parameters for the market
     */
    function initialize(
        PredictionMarketInitialParams calldata params_
    ) external;

    /**
     * @notice Starts the waiting result state for a specific event.
     * @param event_ The event to transition to waiting result state
     */
    function startWaitingResultState(PredictionMarketEvent event_) external;

    /**
     * @notice Processes the result of an event and updates its status accordingly.
     * @param event_ The event to process
     * @param result_ The result of the event (true for "yes", false for "no")
     */
    function proccessEventResult(
        PredictionMarketEvent event_,
        bool result_
    ) external;

    /**
     * @notice Purchases "yes" shares for a specific event.
     * @param event_ The event for which shares are being purchased
     * @param sharesAmount_ The number of shares to purchase
     * @return depositAmount The amount of tokens deposited for the purchase
     */
    function buyYesShares(
        PredictionMarketEvent event_,
        uint256 sharesAmount_
    ) external returns (uint256 depositAmount);

    /**
     * @notice Purchases "no" shares for a specific event.
     * @param event_ The event for which shares are being purchased
     * @param sharesAmount_ The number of shares to purchase
     * @return depositAmount The total amount of tokens deposited, including fees
     * @return fee The fee applied to the purchase
     */
    function buyNoShares(
        PredictionMarketEvent event_,
        uint256 sharesAmount_
    ) external returns (uint256 depositAmount, uint256 fee);

    /**
     * @notice Withdraws accumulated fees for a specific event.
     * @param event_ The event for which fees are being withdrawn
     */
    function withdrawFee(PredictionMarketEvent event_) external;

    /**
     * @notice Claims rewards for a specific event based on the user's shares.
     * @param event_ The event for which rewards are being claimed
     */
    function claim(PredictionMarketEvent event_) external;

    /**
     * @notice Retrieves the amount available to claim for a user for a specific event.
     * @param user_ The address of the user
     * @param event_ The event for which the claimable amount is being calculated
     * @return amountOut The total amount available to claim for the user
     * @return fee The fee applicable on the claim (if any)
     */
    function getAvailableToClaim(
        address user_,
        PredictionMarketEvent event_
    ) external view returns (uint256 amountOut, uint256 fee);

    /**
     * @notice Retrieves the balance details of a user for a specific event.
     * @param user_ The address of the user
     * @param event_ The event for which balances are being queried
     * @return The user's balance details as a `UserBalance` struct
     */
    function getUserBalances(
        address user_,
        PredictionMarketEvent event_
    ) external view returns (UserBalance memory);

    /**
     * @notice Returns the currently active event in the market.
     * @return The active event as a `PredictionMarketEvent`
     */
    function getActiveEvent() external view returns (PredictionMarketEvent);

    /**
     * @notice Calculates the deposit amount required to buy "no" shares, including fees.
     * @param sharesAmount_ The number of "no" shares to purchase
     * @return depositAmountInWithFee The total deposit amount including fees
     * @return depositAmountIn The deposit amount excluding fees
     */
    function calculateNoSharesDepositAmountIn(
        uint256 sharesAmount_
    )
        external
        view
        returns (uint256 depositAmountInWithFee, uint256 depositAmountIn);

    /**
     * @notice Calculates the deposit amount required to buy "yes" shares.
     * @param sharesAmount_ The number of "yes" shares to purchase
     * @return The required deposit amount
     */
    function calculateYesDepositAmountIn(
        uint256 sharesAmount_
    ) external view returns (uint256);

    /**
     * @notice Checks if the current active event is in the waiting result state.
     * @return True if the active event is waiting for a result, otherwise false
     */
    function isWaitingResult() external view returns (bool);
}

File 14 of 15 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

uint256 constant PRECISION = 1e18;

File 15 of 15 : MarketStruct.sol
// SPDX-License-Identifier: MIT
pragma solidity =0.8.28;

enum PredictionMarketEvent {
    REACH_POOL,
    REACH_500_000_CAP,
    REACH_1_000_000_CAP,
    REACH_2_000_000_CAP,
    REACH_5_000_000_CAP
}

struct PredictionMarketInitialParams {
    address token;
    uint256 basePrice;
    uint256 noBuyFeePercentage;
    uint256 yesClaimFeePercentage;
    uint256 eventDuration;
    PredictionMarketEvent initialEvent;
}

struct PredictionMarketCreateParams {
    address token;
    PredictionMarketEvent initialEvent;
    uint256 deadline;
    bytes signature;
}

Settings
{
  "viaIR": true,
  "optimizer": {
    "enabled": true,
    "runs": 2000
  },
  "metadata": {
    "bytecodeHash": "none"
  },
  "evmVersion": "paris",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"EventNotAvailable","type":"error"},{"inputs":[{"internalType":"enum PredictionMarketEvent","name":"marketEvent","type":"uint8"}],"name":"InvalidActiveEvent","type":"error"},{"inputs":[],"name":"InvalidBlockSkip","type":"error"},{"inputs":[],"name":"InvalidEventStatus","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"UnauthorizedOracle","type":"error"},{"inputs":[],"name":"WaitingResult","type":"error"},{"inputs":[],"name":"ZeroAmountToClaim","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"enum PredictionMarketEvent","name":"marketEvent","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"depositAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesAmountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesFee","type":"uint256"}],"name":"BuyNoShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"enum PredictionMarketEvent","name":"marketEvent","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"depositAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sharesAmountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"BuyYesShares","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"enum PredictionMarketEvent","name":"marketEvent","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"enum PredictionMarketEvent","name":"marketEvent","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"}],"name":"NextActiveEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"enum PredictionMarketEvent","name":"marketEvent","type":"uint8"},{"indexed":true,"internalType":"enum IPredictionMarket.EventStatus","name":"status","type":"uint8"}],"name":"UpdateEventStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"enum PredictionMarketEvent","name":"marketEvent","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"WithdrawFee","type":"event"},{"inputs":[],"name":"basePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum PredictionMarketEvent","name":"event_","type":"uint8"},{"internalType":"uint256","name":"sharesAmount_","type":"uint256"}],"name":"buyNoShares","outputs":[{"internalType":"uint256","name":"depositAmount","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum PredictionMarketEvent","name":"event_","type":"uint8"},{"internalType":"uint256","name":"sharesAmount_","type":"uint256"}],"name":"buyYesShares","outputs":[{"internalType":"uint256","name":"depositAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sharesAmount_","type":"uint256"}],"name":"calculateNoSharesDepositAmountIn","outputs":[{"internalType":"uint256","name":"depositAmountInWithFee","type":"uint256"},{"internalType":"uint256","name":"depositAmountIn","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sharesAmount_","type":"uint256"}],"name":"calculateYesDepositAmountIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum PredictionMarketEvent","name":"event_","type":"uint8"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eventDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum PredictionMarketEvent","name":"","type":"uint8"}],"name":"events","outputs":[{"internalType":"enum IPredictionMarket.EventStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"startWaitingResultBlock","type":"uint256"},{"internalType":"uint256","name":"yesSharesTotal","type":"uint256"},{"internalType":"uint256","name":"noSharesTotal","type":"uint256"},{"internalType":"uint256","name":"yesDepositTotal","type":"uint256"},{"internalType":"uint256","name":"noDepositTotal","type":"uint256"},{"internalType":"uint256","name":"feeAccumulated","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getActiveEvent","outputs":[{"internalType":"enum PredictionMarketEvent","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"enum PredictionMarketEvent","name":"event_","type":"uint8"}],"name":"getAvailableToClaim","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"enum PredictionMarketEvent","name":"event_","type":"uint8"}],"name":"getUserBalances","outputs":[{"components":[{"internalType":"uint256","name":"noDepoosit","type":"uint256"},{"internalType":"uint256","name":"yesDeposit","type":"uint256"},{"internalType":"uint256","name":"noShares","type":"uint256"},{"internalType":"uint256","name":"yesShares","type":"uint256"},{"internalType":"uint256","name":"claimed","type":"uint256"}],"internalType":"struct IPredictionMarket.UserBalance","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"basePrice","type":"uint256"},{"internalType":"uint256","name":"noBuyFeePercentage","type":"uint256"},{"internalType":"uint256","name":"yesClaimFeePercentage","type":"uint256"},{"internalType":"uint256","name":"eventDuration","type":"uint256"},{"internalType":"enum PredictionMarketEvent","name":"initialEvent","type":"uint8"}],"internalType":"struct PredictionMarketInitialParams","name":"params_","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isWaitingResult","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"noBuyFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum PredictionMarketEvent","name":"event_","type":"uint8"},{"internalType":"bool","name":"result_","type":"bool"}],"name":"proccessEventResult","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum PredictionMarketEvent","name":"event_","type":"uint8"}],"name":"startWaitingResultState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum PredictionMarketEvent","name":"event_","type":"uint8"}],"name":"withdrawFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"yesClaimFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

6080806040523460d2577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460ff8160401c1660c1576002600160401b03196001600160401b03821601605c575b60405161192090816100d88239f35b6001600160401b0319166001600160401b039081177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005581527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a13880604d565b63f92ee8a960e01b60005260046000fd5b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816301352be71461120a575080631695d7fa146111ec5780631c4825aa146111ce5780632a3fac76146111b05780633174cf24146111925780633742a3661461116d57806346982153146110a957806351b705b614610e8f5780636c91b9aa14610ced578063919ad1d414610c56578063940dca6014610ab957806395d4063f146109035780639a95680a14610709578063ae08b547146106e4578063c45a0155146106bd578063c7876ea41461069f578063ec7e2f9f14610368578063ec93ffe21461015a578063fc0c546a146101335763fe077066146100fb57600080fd5b3461012e57602060031936011261012e576020670de0b6b3a7640000610125600254600435611289565b04604051908152f35b600080fd5b3461012e57600060031936011261012e5760206001600160a01b0360015416604051908152f35b3461012e57602060031936011261012e57600435600581101561012e57602460206001600160a01b0360005416604051928380927fa97e5c930000000000000000000000000000000000000000000000000000000082523360048301525afa90811561035c5760009161032d575b50156102ff578060ff600754166101de82611231565b6101e781611231565b036102c9576101f581611231565b806000526006602052600160ff6040600020541661021281611231565b0361029f5761022081611231565b8060005260066020524360026040600020015561023c81611231565b6000818152600660205260409020600260ff1982541617905561025e81611231565b600290337fdfe6721fca75c7d4b3fdc98fed58aebf3d31de8bcc843f5d21568a877d0e87d8600080a4005b634e487b7160e01b600052602160045260246000fd5b7fff0d5b170000000000000000000000000000000000000000000000000000000060005260046000fd5b7f59b31eee000000000000000000000000000000000000000000000000000000006000526102f681611231565b60045260246000fd5b7f91e47843000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b61034f915060203d602011610355575b6103478183611518565b810190611559565b826101c8565b503d61033d565b6040513d6000823e3d90fd5b3461012e5760c060031936011261012e5760007ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460ff8160401c16159067ffffffffffffffff811680159081610697575b600114908161068d575b159081610684575b5061065c578160017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008316177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055610607575b50337fffffffffffffffffffffffff00000000000000000000000000000000000000008354161782556004356001600160a01b038116809103610603577fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015560243560025560443560035560643560045560843560055560a4356005811015610603576104b281611231565b6000815b60ff81166004811161055c576104cb81611231565b6104d481611231565b8086526006602052604086208361054857600160ff19825416179055826104fa82611231565b6105345790600161052f92337fdfe6721fca75c7d4b3fdc98fed58aebf3d31de8bcc843f5d21568a877d0e87d88980a4611571565b6104b6565b602486634e487b7160e01b81526021600452fd5b602487634e487b7160e01b81526021600452fd5b5050506105689061167a565b61056f5780f35b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b8280fd5b7fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00558261041f565b6004837ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b905015846103cc565b303b1591506103c4565b8391506103ba565b3461012e57600060031936011261012e576020600254604051908152f35b3461012e57600060031936011261012e5760206001600160a01b0360005416604051908152f35b3461012e57600060031936011261012e5760206106ff6115a1565b6040519015158152f35b3461012e576107173661126a565b90600460206001600160a01b036000541660405192838092635c975abb60e01b82525afa90811561035c576000916108e4575b506108ba5760409161075b826116f1565b610764816112c9565b61077c8261077395939561177d565b309033906117f1565b6107868185611305565b9261079083611231565b826000526006602052600886600020016001600160a01b033316600052602052600286600020016107c28282546112bc565b90556107cd83611231565b826000526006602052600886600020016001600160a01b033316600052602052856000206107fc8382546112bc565b905561080783611231565b826000526006602052600686600020016108228382546112bc565b905561082d83611231565b826000526006602052600486600020016108488282546112bc565b905561085383611231565b8260005260066020526007866000200161086e8582546112bc565b905561087983611231565b8551918252602082015282858201527fbc622334e935de3fa270d9342713790af0110c962cd3e1bdd447d3c80ec6a3f960603392a382519182526020820152f35b7fd93c06650000000000000000000000000000000000000000000000000000000060005260046000fd5b6108fd915060203d602011610355576103478183611518565b8361074a565b3461012e57602060031936011261012e57600435600581101561012e57600460206001600160a01b036000541660405192838092635c975abb60e01b82525afa90811561035c57600091610a9a575b506108ba5761096081611231565b80600052600660205260ff6040600020541661097b81611231565b60048114159081610a85575b5061029f576109968133611312565b8115610a5b576109a583611231565b82600052600660205260086040600020016001600160a01b03331660005260205260046040600020016109d98382546112bc565b905580610a2f575b6109f382336109ee61177d565b611852565b6109fc83611231565b60405191825260208201527f871814d00026fb0b90f782f98fba24a7d13b90b24662951b7594901d4adc3c8960403392a3005b610a3883611231565b8260005260066020526007604060002001610a548282546112bc565b90556109e1565b7f42a6b83f0000000000000000000000000000000000000000000000000000000060005260046000fd5b60039150610a9281611231565b141582610987565b610ab3915060203d602011610355576103478183611518565b82610952565b3461012e57602060031936011261012e57600435600581101561012e57600460206001600160a01b0360005416604051928380927f8cfc2ef30000000000000000000000000000000000000000000000000000000082525afa90811561035c57600091610c27575b506001600160a01b0333911603610bfd57610b3b81611231565b80600052600660205260ff60406000205416610b5681611231565b60048114159081610be8575b5061029f57610b7081611231565b8060005260066020526007604060002001548015610a5b57610b9182611231565b81600052600660205260006007604082200155610bb181336109ee61177d565b610bba82611231565b6040519081527fb6cebe2d384e65c6f64630e62ab819d8b662b126c7514d686526ce34a594bc9b60203392a3005b60039150610bf581611231565b141582610b62565b7f4ca888670000000000000000000000000000000000000000000000000000000060005260046000fd5b610c49915060203d602011610c4f575b610c418183611518565b810190611582565b82610b21565b503d610c37565b3461012e57602060031936011261012e57600435600581101561012e57610c7c81611231565b6000526006602052610100604060002060ff8154169060018101549060028101546003820154600483015490600584015492600760068601549501549560405197610cc681611231565b8852602088015260408701526060860152608085015260a084015260c083015260e0820152f35b3461012e57610cfb3661126a565b600460206001600160a01b036000541660405192838092635c975abb60e01b82525afa90811561035c57600091610e70575b506108ba5781610d3e6020936116f1565b670de0b6b3a7640000610d5360025484611289565b0491610d618361077361177d565b610d6a82611231565b816000526006845260086040600020016001600160a01b03331660005284526003604060002001610d9c8282546112bc565b9055610da782611231565b816000526006845260086040600020016001600160a01b03331660005284526001604060002001610dd98482546112bc565b9055610de482611231565b81600052600684526005604060002001610dff8482546112bc565b9055610e0a82611231565b81600052600684526003604060002001610e258282546112bc565b9055610e3082611231565b6040519083825284820152600060408201527fedf6256095ac8a82e207dc47d2fd0514466a3bc75e6eedac223f5ad54f7206b560603392a3604051908152f35b610e89915060203d602011610355576103478183611518565b83610d2d565b3461012e57604060031936011261012e57600435600581101561012e57602435801515810361012e57602460206001600160a01b0360005416604051928380927fa97e5c930000000000000000000000000000000000000000000000000000000082523360048301525afa90811561035c5760009161108a575b50156102ff578160ff60075416610f1f82611231565b610f2881611231565b0361105c57610f3682611231565b816000526006602052600260ff60406000205416610f5381611231565b0361029f57610f6182611231565b816000526006602052600260406000200154431115611032571561102b5760035b610f8b81611231565b60038103610ff457610f9d9082611613565b610fa681611231565b60048103610fb1575b005b60ff81610fbf600193611231565b160160ff8111610fde5760ff610faf9116610fd981611231565b61167a565b634e487b7160e01b600052601160045260246000fd5b90610ffe81611231565b60ff81169060048211610faf57611021838361101c61102695611231565b611613565b611571565b610ffe565b6004610f82565b7f42c5e7100000000000000000000000000000000000000000000000000000000060005260046000fd5b507f59b31eee000000000000000000000000000000000000000000000000000000006000526102f681611231565b6110a3915060203d602011610355576103478183611518565b83610f09565b3461012e5760a060406110bb3661123b565b6000608084516110ca816114e6565b828152826020820152828682015282606082015201526110e981611231565b6000526006602052600882600020016001600160a01b0360009216825260205220604051611116816114e6565b81549182825260018101546020830190815260028201549060408401918252608060046003850154946060870195865201549401938452604051948552516020850152516040840152516060830152516080820152f35b3461012e5760406111866111803661123b565b90611312565b82519182526020820152f35b3461012e57600060031936011261012e576020600554604051908152f35b3461012e57600060031936011261012e576020600354604051908152f35b3461012e57602060031936011261012e5760406111866004356112c9565b3461012e57600060031936011261012e576020600454604051908152f35b3461012e57600060031936011261012e5760209060ff6007541661122d81611231565b8152f35b6005111561028957565b600319604091011261012e576004356001600160a01b038116810361012e5790602435600581101561012e5790565b600319604091011261012e57600435600581101561012e579060243590565b81810292918115918404141715610fde57565b81156112a6570490565b634e487b7160e01b600052601260045260246000fd5b91908201809211610fde57565b90670de0b6b3a7640000611301600254826112fa6112f4826112ed60035486611289565b04846112bc565b87611289565b0494611289565b0490565b91908203918211610fde57565b60009291839161132182611231565b81600052600660205260ff604060002054169061133d83611231565b82600052600660205260086040600020016001600160a01b0333166000526020526004604060002001546114d85761137483611231565b8260005260066020526113aa60056040600020015461139285611231565b846000526006602052600660406000200154906112bc565b9182156114c9576113ba81611231565b6004810361142b57506114289394955090611407916113d884611231565b8360005260066020526001600160a01b0360086040600020019116600052602052600260406000200154611289565b9061141181611231565b60005260066020526004604060002001549061129c565b91565b93949360039061143a81611231565b1461144457505050565b829395506114a9945061145961148893611231565b8360005260066020526001600160a01b0360086040600020019116600052602052600360406000200154611289565b9061149281611231565b60005260066020526003604060002001549061129c565b611428670de0b6b3a76400006114c160045484611289565b048092611305565b50505050509050600090600090565b505050509050600090600090565b60a0810190811067ffffffffffffffff82111761150257604052565b634e487b7160e01b600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761150257604052565b9081602091031261012e5751801515810361012e5790565b60ff1660ff8114610fde5760010190565b9081602091031261012e57516001600160a01b038116810361012e5790565b60ff600754166115b081611231565b80600052600660205260ff604060002054166115cb81611231565b6002811461160c576001906115df81611231565b1490816115ea575090565b90506115f581611231565b600052600660205260016040600020015442101590565b5050600190565b61161c81611231565b806000526006602052604060002061163383611231565b60ff1981541660ff841617905561164981611231565b61165282611231565b337fdfe6721fca75c7d4b3fdc98fed58aebf3d31de8bcc843f5d21568a877d0e87d8600080a4565b61168381611231565b60ff196007541660ff82161760075561169e600554426112bc565b6116a782611231565b816000526006602052806001604060002001556116c382611231565b6040519081527f047dc20fe3f5960b48e20ed93b94936aefaf413279bbe0bef65a9e085db61d5b60203392a3565b6116f96115a1565b6117535761170681611231565b6000526006602052600160ff6040600020541661172281611231565b0361172957565b7f4a7c9c460000000000000000000000000000000000000000000000000000000060005260046000fd5b7f475bf7e20000000000000000000000000000000000000000000000000000000060005260046000fd5b600460206001600160a01b0360005416604051928380927fc89039c50000000000000000000000000000000000000000000000000000000082525afa801561035c576001600160a01b03916000916117d457501690565b6117ed915060203d602011610c4f57610c418183611518565b1690565b9091926001600160a01b036118509481604051957f23b872dd00000000000000000000000000000000000000000000000000000000602088015216602486015216604484015260648301526064825261184b608483611518565b6118a2565b565b611850926001600160a01b03604051937fa9059cbb00000000000000000000000000000000000000000000000000000000602086015216602484015260448301526044825261184b606483611518565b906000602091828151910182855af11561035c576000513d61190a57506001600160a01b0381163b155b6118d35750565b6001600160a01b03907f5274afe7000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b600114156118cc56fea164736f6c634300081c000a

Deployed Bytecode

0x608080604052600436101561001357600080fd5b60003560e01c90816301352be71461120a575080631695d7fa146111ec5780631c4825aa146111ce5780632a3fac76146111b05780633174cf24146111925780633742a3661461116d57806346982153146110a957806351b705b614610e8f5780636c91b9aa14610ced578063919ad1d414610c56578063940dca6014610ab957806395d4063f146109035780639a95680a14610709578063ae08b547146106e4578063c45a0155146106bd578063c7876ea41461069f578063ec7e2f9f14610368578063ec93ffe21461015a578063fc0c546a146101335763fe077066146100fb57600080fd5b3461012e57602060031936011261012e576020670de0b6b3a7640000610125600254600435611289565b04604051908152f35b600080fd5b3461012e57600060031936011261012e5760206001600160a01b0360015416604051908152f35b3461012e57602060031936011261012e57600435600581101561012e57602460206001600160a01b0360005416604051928380927fa97e5c930000000000000000000000000000000000000000000000000000000082523360048301525afa90811561035c5760009161032d575b50156102ff578060ff600754166101de82611231565b6101e781611231565b036102c9576101f581611231565b806000526006602052600160ff6040600020541661021281611231565b0361029f5761022081611231565b8060005260066020524360026040600020015561023c81611231565b6000818152600660205260409020600260ff1982541617905561025e81611231565b600290337fdfe6721fca75c7d4b3fdc98fed58aebf3d31de8bcc843f5d21568a877d0e87d8600080a4005b634e487b7160e01b600052602160045260246000fd5b7fff0d5b170000000000000000000000000000000000000000000000000000000060005260046000fd5b7f59b31eee000000000000000000000000000000000000000000000000000000006000526102f681611231565b60045260246000fd5b7f91e47843000000000000000000000000000000000000000000000000000000006000523360045260246000fd5b61034f915060203d602011610355575b6103478183611518565b810190611559565b826101c8565b503d61033d565b6040513d6000823e3d90fd5b3461012e5760c060031936011261012e5760007ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460ff8160401c16159067ffffffffffffffff811680159081610697575b600114908161068d575b159081610684575b5061065c578160017fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008316177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055610607575b50337fffffffffffffffffffffffff00000000000000000000000000000000000000008354161782556004356001600160a01b038116809103610603577fffffffffffffffffffffffff0000000000000000000000000000000000000000600154161760015560243560025560443560035560643560045560843560055560a4356005811015610603576104b281611231565b6000815b60ff81166004811161055c576104cb81611231565b6104d481611231565b8086526006602052604086208361054857600160ff19825416179055826104fa82611231565b6105345790600161052f92337fdfe6721fca75c7d4b3fdc98fed58aebf3d31de8bcc843f5d21568a877d0e87d88980a4611571565b6104b6565b602486634e487b7160e01b81526021600452fd5b602487634e487b7160e01b81526021600452fd5b5050506105689061167a565b61056f5780f35b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b8280fd5b7fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00558261041f565b6004837ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b905015846103cc565b303b1591506103c4565b8391506103ba565b3461012e57600060031936011261012e576020600254604051908152f35b3461012e57600060031936011261012e5760206001600160a01b0360005416604051908152f35b3461012e57600060031936011261012e5760206106ff6115a1565b6040519015158152f35b3461012e576107173661126a565b90600460206001600160a01b036000541660405192838092635c975abb60e01b82525afa90811561035c576000916108e4575b506108ba5760409161075b826116f1565b610764816112c9565b61077c8261077395939561177d565b309033906117f1565b6107868185611305565b9261079083611231565b826000526006602052600886600020016001600160a01b033316600052602052600286600020016107c28282546112bc565b90556107cd83611231565b826000526006602052600886600020016001600160a01b033316600052602052856000206107fc8382546112bc565b905561080783611231565b826000526006602052600686600020016108228382546112bc565b905561082d83611231565b826000526006602052600486600020016108488282546112bc565b905561085383611231565b8260005260066020526007866000200161086e8582546112bc565b905561087983611231565b8551918252602082015282858201527fbc622334e935de3fa270d9342713790af0110c962cd3e1bdd447d3c80ec6a3f960603392a382519182526020820152f35b7fd93c06650000000000000000000000000000000000000000000000000000000060005260046000fd5b6108fd915060203d602011610355576103478183611518565b8361074a565b3461012e57602060031936011261012e57600435600581101561012e57600460206001600160a01b036000541660405192838092635c975abb60e01b82525afa90811561035c57600091610a9a575b506108ba5761096081611231565b80600052600660205260ff6040600020541661097b81611231565b60048114159081610a85575b5061029f576109968133611312565b8115610a5b576109a583611231565b82600052600660205260086040600020016001600160a01b03331660005260205260046040600020016109d98382546112bc565b905580610a2f575b6109f382336109ee61177d565b611852565b6109fc83611231565b60405191825260208201527f871814d00026fb0b90f782f98fba24a7d13b90b24662951b7594901d4adc3c8960403392a3005b610a3883611231565b8260005260066020526007604060002001610a548282546112bc565b90556109e1565b7f42a6b83f0000000000000000000000000000000000000000000000000000000060005260046000fd5b60039150610a9281611231565b141582610987565b610ab3915060203d602011610355576103478183611518565b82610952565b3461012e57602060031936011261012e57600435600581101561012e57600460206001600160a01b0360005416604051928380927f8cfc2ef30000000000000000000000000000000000000000000000000000000082525afa90811561035c57600091610c27575b506001600160a01b0333911603610bfd57610b3b81611231565b80600052600660205260ff60406000205416610b5681611231565b60048114159081610be8575b5061029f57610b7081611231565b8060005260066020526007604060002001548015610a5b57610b9182611231565b81600052600660205260006007604082200155610bb181336109ee61177d565b610bba82611231565b6040519081527fb6cebe2d384e65c6f64630e62ab819d8b662b126c7514d686526ce34a594bc9b60203392a3005b60039150610bf581611231565b141582610b62565b7f4ca888670000000000000000000000000000000000000000000000000000000060005260046000fd5b610c49915060203d602011610c4f575b610c418183611518565b810190611582565b82610b21565b503d610c37565b3461012e57602060031936011261012e57600435600581101561012e57610c7c81611231565b6000526006602052610100604060002060ff8154169060018101549060028101546003820154600483015490600584015492600760068601549501549560405197610cc681611231565b8852602088015260408701526060860152608085015260a084015260c083015260e0820152f35b3461012e57610cfb3661126a565b600460206001600160a01b036000541660405192838092635c975abb60e01b82525afa90811561035c57600091610e70575b506108ba5781610d3e6020936116f1565b670de0b6b3a7640000610d5360025484611289565b0491610d618361077361177d565b610d6a82611231565b816000526006845260086040600020016001600160a01b03331660005284526003604060002001610d9c8282546112bc565b9055610da782611231565b816000526006845260086040600020016001600160a01b03331660005284526001604060002001610dd98482546112bc565b9055610de482611231565b81600052600684526005604060002001610dff8482546112bc565b9055610e0a82611231565b81600052600684526003604060002001610e258282546112bc565b9055610e3082611231565b6040519083825284820152600060408201527fedf6256095ac8a82e207dc47d2fd0514466a3bc75e6eedac223f5ad54f7206b560603392a3604051908152f35b610e89915060203d602011610355576103478183611518565b83610d2d565b3461012e57604060031936011261012e57600435600581101561012e57602435801515810361012e57602460206001600160a01b0360005416604051928380927fa97e5c930000000000000000000000000000000000000000000000000000000082523360048301525afa90811561035c5760009161108a575b50156102ff578160ff60075416610f1f82611231565b610f2881611231565b0361105c57610f3682611231565b816000526006602052600260ff60406000205416610f5381611231565b0361029f57610f6182611231565b816000526006602052600260406000200154431115611032571561102b5760035b610f8b81611231565b60038103610ff457610f9d9082611613565b610fa681611231565b60048103610fb1575b005b60ff81610fbf600193611231565b160160ff8111610fde5760ff610faf9116610fd981611231565b61167a565b634e487b7160e01b600052601160045260246000fd5b90610ffe81611231565b60ff81169060048211610faf57611021838361101c61102695611231565b611613565b611571565b610ffe565b6004610f82565b7f42c5e7100000000000000000000000000000000000000000000000000000000060005260046000fd5b507f59b31eee000000000000000000000000000000000000000000000000000000006000526102f681611231565b6110a3915060203d602011610355576103478183611518565b83610f09565b3461012e5760a060406110bb3661123b565b6000608084516110ca816114e6565b828152826020820152828682015282606082015201526110e981611231565b6000526006602052600882600020016001600160a01b0360009216825260205220604051611116816114e6565b81549182825260018101546020830190815260028201549060408401918252608060046003850154946060870195865201549401938452604051948552516020850152516040840152516060830152516080820152f35b3461012e5760406111866111803661123b565b90611312565b82519182526020820152f35b3461012e57600060031936011261012e576020600554604051908152f35b3461012e57600060031936011261012e576020600354604051908152f35b3461012e57602060031936011261012e5760406111866004356112c9565b3461012e57600060031936011261012e576020600454604051908152f35b3461012e57600060031936011261012e5760209060ff6007541661122d81611231565b8152f35b6005111561028957565b600319604091011261012e576004356001600160a01b038116810361012e5790602435600581101561012e5790565b600319604091011261012e57600435600581101561012e579060243590565b81810292918115918404141715610fde57565b81156112a6570490565b634e487b7160e01b600052601260045260246000fd5b91908201809211610fde57565b90670de0b6b3a7640000611301600254826112fa6112f4826112ed60035486611289565b04846112bc565b87611289565b0494611289565b0490565b91908203918211610fde57565b60009291839161132182611231565b81600052600660205260ff604060002054169061133d83611231565b82600052600660205260086040600020016001600160a01b0333166000526020526004604060002001546114d85761137483611231565b8260005260066020526113aa60056040600020015461139285611231565b846000526006602052600660406000200154906112bc565b9182156114c9576113ba81611231565b6004810361142b57506114289394955090611407916113d884611231565b8360005260066020526001600160a01b0360086040600020019116600052602052600260406000200154611289565b9061141181611231565b60005260066020526004604060002001549061129c565b91565b93949360039061143a81611231565b1461144457505050565b829395506114a9945061145961148893611231565b8360005260066020526001600160a01b0360086040600020019116600052602052600360406000200154611289565b9061149281611231565b60005260066020526003604060002001549061129c565b611428670de0b6b3a76400006114c160045484611289565b048092611305565b50505050509050600090600090565b505050509050600090600090565b60a0810190811067ffffffffffffffff82111761150257604052565b634e487b7160e01b600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761150257604052565b9081602091031261012e5751801515810361012e5790565b60ff1660ff8114610fde5760010190565b9081602091031261012e57516001600160a01b038116810361012e5790565b60ff600754166115b081611231565b80600052600660205260ff604060002054166115cb81611231565b6002811461160c576001906115df81611231565b1490816115ea575090565b90506115f581611231565b600052600660205260016040600020015442101590565b5050600190565b61161c81611231565b806000526006602052604060002061163383611231565b60ff1981541660ff841617905561164981611231565b61165282611231565b337fdfe6721fca75c7d4b3fdc98fed58aebf3d31de8bcc843f5d21568a877d0e87d8600080a4565b61168381611231565b60ff196007541660ff82161760075561169e600554426112bc565b6116a782611231565b816000526006602052806001604060002001556116c382611231565b6040519081527f047dc20fe3f5960b48e20ed93b94936aefaf413279bbe0bef65a9e085db61d5b60203392a3565b6116f96115a1565b6117535761170681611231565b6000526006602052600160ff6040600020541661172281611231565b0361172957565b7f4a7c9c460000000000000000000000000000000000000000000000000000000060005260046000fd5b7f475bf7e20000000000000000000000000000000000000000000000000000000060005260046000fd5b600460206001600160a01b0360005416604051928380927fc89039c50000000000000000000000000000000000000000000000000000000082525afa801561035c576001600160a01b03916000916117d457501690565b6117ed915060203d602011610c4f57610c418183611518565b1690565b9091926001600160a01b036118509481604051957f23b872dd00000000000000000000000000000000000000000000000000000000602088015216602486015216604484015260648301526064825261184b608483611518565b6118a2565b565b611850926001600160a01b03604051937fa9059cbb00000000000000000000000000000000000000000000000000000000602086015216602484015260448301526044825261184b606483611518565b906000602091828151910182855af11561035c576000513d61190a57506001600160a01b0381163b155b6118d35750565b6001600160a01b03907f5274afe7000000000000000000000000000000000000000000000000000000006000521660045260246000fd5b600114156118cc56fea164736f6c634300081c000a

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.