ETH Price: $1,971.93 (+0.11%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Claim244970292026-02-20 9:00:352 days ago1771578035IN
0x71B7a172...5dC67D06D
0 ETH0.000004130.04128251
Vote With Permit...244969822026-02-20 8:51:112 days ago1771577471IN
0x71B7a172...5dC67D06D
0 ETH0.000298112.04108158
Vote With Permit...244969782026-02-20 8:50:232 days ago1771577423IN
0x71B7a172...5dC67D06D
0 ETH0.000298692.0451623
Vote With Permit...244965712026-02-20 7:28:112 days ago1771572491IN
0x71B7a172...5dC67D06D
0 ETH0.000263092.04046368
Vote With Permit...244965532026-02-20 7:24:352 days ago1771572275IN
0x71B7a172...5dC67D06D
0 ETH0.00033242.03724023

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Predicter

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 200 runs

Other Settings:
prague EvmVersion
// SPDX-License-Identifier: MIT
// Envelop V2 — Simple Price Prediction (Voting) Implementation

pragma solidity ^0.8.24;

import {ERC6909TokenSupply} from "@openzeppelin/contracts/token/ERC6909/extensions/ERC6909TokenSupply.sol";

import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/// forge-lint: disable-next-line(unaliased-plain-import)
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// forge-lint: disable-next-line(unaliased-plain-import)
import "../interfaces/IEnvelopOracle.sol";

/// forge-lint: disable-next-line(unaliased-plain-import)
//import "../interfaces/IPermit2Minimal.sol";

import "../interfaces/IPermit2Minimal.sol";

/**
 * @title Predicter
 * @notice A decentralized binary prediction market based on ERC-6909 share tokens.
 *         Each prediction is created by an initiator (“creator”) and users may vote
 *         “agree” or “disagree” by staking ERC20 tokens. After expiration, the prediction
 *         resolves via an on-chain oracle, and winners claim rewards proportional to their share.
 *
 * @dev Uses ERC6909 token IDs encoded as:
 *      [20 bytes: creator address][11 bytes: padding][1 byte: vote flag (1 = yes, 0 = no)]
 *
 * @custom:security-contact Envelop V2
 */
contract Predicter is ERC6909TokenSupply, ReentrancyGuard {
    using SafeERC20 for IERC20;

    /**
     * @dev Single prediction entity created by an address.
     * A creator may have only one active prediction at a time.
     *
     * - strike: token/amount encoded pair representing the stake size for each vote.
     * - predictedPrice: prediction target value (amount field) with token as “unit” asset.
     * - expirationTime: UNIX timestamp after which new votes are not accepted.
     * - resolvedPrice: oracle result after expiration, stored once and used for outcome.
     * - portfolio: array of underlying assets used by the oracle for pricing.
     */

    struct Prediction {
        CompactAsset strike;
        /// @dev predictedPrice.amount is a threshold value.
        ///      Outcome is YES if predictedPrice.amount <= resolvedPrice, otherwise NO.
        ///      resolvedPrice must be in the same units/decimals as predictedPrice.amount.
        OracleData predictedPrice;
        uint40 expirationTime;
        uint96 resolvedPrice;
        CompactAsset[] portfolio;
    }

    // ==================================
    //            CONSTANTS
    // ==================================

    /// @dev Reserved constant for potential “stop voting before expiration” logic.
    uint40 public constant STOP_BEFORE_EXPIRED = 0;

    /// @dev Creator fee in bps. 200 = 2%
    uint96 public constant FEE_CREATOR_PERCENT = 100;

    /// @dev Protocol fee  in bps. 100 = 1%.
    uint96 public constant FEE_PROTOCOL_PERCENT = 0;

    /// @dev Percentage denominator (basis points = 10_000).
    uint96 public constant PERCENT_DENOMINATOR = 10_000;

    /// @dev Fixed-point scale used to reduce rounding loss in share calculations.
    ///      Shares are computed as: userShares = (userBalance * SCALE) / totalWin.
    uint96 public constant SCALE = 1e18;

    /// @dev Hard cap for number of portfolio items, to avoid gas blow-ups.
    uint256 public constant MAX_PORTFOLIO_LEN = 100;

    /// @dev Hard cap for number of portfolio items, to avoid gas blow-ups.
    uint40 public constant MAX_PREDICTION_PERIOD = uint40(1000 days);

    /// @dev Uniswap Permit2 constant (reserved for future integrations, currently unused).
    address public constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /// @dev for use with Uniswap Permit2, seconds
    uint256 public constant PERMIT2_TTL = 600;

    /// @notice Protocol-level fee receiver.
    address public immutable FEE_PROTOCOL_BENEFICIARY;

    /// @notice Oracle used for resolving predictions.
    //address public immutable ORACLE;

    /// @notice Mapping of prediction creator → prediction data.
    mapping(address creator => Prediction) public predictions;

    // ==================================
    //            ERRORS
    // ==================================

    /// @dev Thrown when a creator attempts to create a second active prediction.
    error ActivePredictionExist(address sender);

    /// @dev Thrown when voting happens after expiration.
    error PredictionExpired(address prediction, uint40 expirationTime);

    /// @dev Thrown if the prediction is referenced but does not exist.
    error PredictionNotExist(address prediction);

    /// @dev Thrown if oracle price does not fit into uint96.
    error OraclePriceTooHigh(uint256 oraclePrice);

    /// @dev Thrown if portfolio length exceeds MAX_PORTFOLIO_LEN.
    error TooManyPortfolioItems(uint256 actualLength);

    /// @dev Thrown if prediction too long
    error TooLongPrediction(uint256 actualTimestamp);

    // ==================================
    //            EVENTS
    // ==================================

    /// @notice Emitted when a new prediction is created.
    event PredictionCreated(address indexed creator, uint40 expirationTime);

    /// @notice Emitted on each user vote.
    event Voted(address indexed voter, address indexed prediction, bool agree);

    /// @notice Emitted once prediction is resolved with oracle price.
    event PredictionResolved(address indexed prediction, uint256 resolvedPrice);

    /// @notice Emitted when a user successfully claims their reward.
    event Claimed(address indexed user, address indexed prediction, uint256 reward);

    // ==================================
    //           CONSTRUCTOR
    // ==================================

    /**
     * @param _feeBeneficiary Address receiving protocol-level fees.
    */
    constructor(address _feeBeneficiary) {
        FEE_PROTOCOL_BENEFICIARY = _feeBeneficiary;
    }

    // ==================================
    //           USER FUNCTIONS
    // ==================================

    /**
     * @notice Create a new prediction owned by msg.sender.
     *         A creator may only have one active prediction at a time.
     * @param _pred The prediction parameters.
     *
     * Requirements:
     * - `_pred.portfolio.length` MUST be ≤ MAX_PORTFOLIO_LEN.
     * - Creator MUST NOT have an existing prediction (`expirationTime == 0`).
     */
    function createPrediction(Prediction calldata _pred) external {
        if (_pred.portfolio.length > MAX_PORTFOLIO_LEN) {
            revert TooManyPortfolioItems(_pred.portfolio.length);
        }

        if (_pred.expirationTime > MAX_PREDICTION_PERIOD + uint40(block.timestamp)) {
            revert TooLongPrediction(_pred.expirationTime);
        }
        _checkPrediction(msg.sender, _pred);
        _createPrediction(msg.sender, _pred);
        emit PredictionCreated(msg.sender, _pred.expirationTime);
    }

    /**
     * @notice Cast a vote on a prediction by staking tokens.
     * @param _prediction Address of the prediction creator.
     * @param _agree Whether the user votes “yes” (true) or “no” (false).
     *
     * Emits:
     * - {Voted}
     *
     * Requirements:
     * - Prediction MUST exist.
     * - Current time MUST be strictly less than `expirationTime`.
     * - Caller MUST have approved enough ERC20 to `this` for the strike amount.
     */
    function vote(address _prediction, bool _agree) external nonReentrant {
        (address token, uint96 amount) = _vote(msg.sender, _prediction, _agree);
        IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
    }

    /**
     * @notice Cast a vote using Uniswap Permit2 signature-based transfer.
     * @dev Flow:
     *  1) User gives standard ERC20 approval to Permit2 once (off-chain setup).
     *  2) Prerpare and sign digest for Permit2.
     *  3) In this method we call Permit2.permitTransferFrom, wich
     *     transfer stake from user to this contract, then mint 6909-shares.
     *
     * @param _prediction Address of the prediction creator.
     * @param _agree      Whether the user votes “yes” (true) or “no” (false).
     * @param permit      Permit2 permit struct (token, max amount, nonce, deadline).
     * @param transfer    Permit2 transfer details (recipient, requestedAmount).
     * @param signature   User EIP-712 signature for Permit2.
     *
     * Requirements:
     * - Prediction MUST exist.
     * - Now MUST be < expirationTime.
     * - permit.permitted.token MUST match prediction strike token.
     * - transfer.to MUST be this contract.
     * - transfer.requestedAmount MUST be >= strike amount.
     * - owner (msg.sender) MUST have given ERC20 approve to Permit2 beforehand.
     */
    function voteWithPermit2(
        address _prediction,
        bool _agree,
        IPermit2Minimal.PermitTransferFrom calldata permit,
        IPermit2Minimal.SignatureTransferDetails calldata transfer,
        bytes calldata signature
    ) external nonReentrant {
        Prediction storage p = predictions[_prediction];

        // Basic sanity checks to bind Permit2 params to this prediction
        if (permit.permitted.token != p.strike.token) {
            revert("Permit2: wrong token");
        }
        if (transfer.to != address(this)) {
            revert("Permit2: wrong recipient");
        }
        if (transfer.requestedAmount != p.strike.amount) {
            revert("Permit2: insufficient amount");
        }

        _vote(msg.sender, _prediction, _agree);

        // 1) Move tokens from user to this contract via Permit2
        IPermit2Minimal(PERMIT2).permitTransferFrom(permit, transfer, msg.sender, signature);
    }

    /**
     * @notice Cast a vote using Uniswap Permit2 signature-based transfer.
     * @dev Flow:
     *  1) User gives standard ERC20 approval to Permit2 once (off-chain setup).
     *  2) Prerpare and sign digest for Permit2.
     *  3) In this method we call Permit2.permitTransferFrom, wich
     *     transfer stake from user to this contract, then mint 6909-shares.
     *
     * @param _prediction Address of the prediction creator.
     * @param _agree      Whether the user votes “yes” (true) or “no” (false).
     * @param permit      Permit2 permit struct (token, max amount, nonce, deadline).
     * @param signature   User EIP-712 signature for Permit2.
     *
     * Requirements:
     * - Prediction MUST exist.
     * - owner (msg.sender) MUST have given ERC20 approve to Permit2 beforehand.
     */
    function voteWithPermit2(
        address _prediction,
        bool _agree,
        IPermit2Minimal.PermitTransferFrom calldata permit,
        bytes calldata signature
    ) external virtual nonReentrant {
        Prediction storage p = predictions[_prediction];

        // Basic sanity checks to bind Permit2 params to this prediction
        if (permit.permitted.token != p.strike.token) {
            revert("Permit2: wrong token");
        }

        IPermit2Minimal.SignatureTransferDetails memory transfer = IPermit2Minimal.SignatureTransferDetails(
            address(this),
            p.strike.amount //TODO check implicit uint256() ?
        );

        _vote(msg.sender, _prediction, _agree);

        // 1) Move tokens from user to this contract via Permit2
        IPermit2Minimal(PERMIT2).permitTransferFrom(permit, transfer, msg.sender, signature);
    }

    /**
     * @dev If only one side has any votes (no-contest), voters can refund their stake instead of rewards.
     * @notice Claim rewards based on resolved prediction outcome.
     * @param _prediction Address of prediction creator.
     *
     * Emits:
     * - {PredictionResolved} (if first-time resolve)
     * - {Claimed} (if caller has a positive winner’s prize)
     *
     * Requirements:
     * - Prediction MUST exist and be resolvable (expired).
     * - Caller MUST hold a positive amount of winning share tokens (ERC6909),
     *   otherwise the call is a no-op.
     */
    function claim(address _prediction) external nonReentrant {
        if (_resolvePrediction(_prediction)) {
            if (_checkIsGameValidAndReturnStakesIfNot(msg.sender, _prediction)) {
                _claim(msg.sender, _prediction);
            }
        }
    }

    /**
     * @notice Helper to estimate user positions and raw rewards without executing a claim.
     * @param _user Address of the user.
     * @param _prediction Address of the prediction creator.
     *
     * @return yesBalance User balance of “yes” 6909 tokens.
     * @return noBalance  User balance of “no” 6909 tokens.
     * @return yesTotal   Total supply of “yes” tokens.
     * @return noTotal    Total supply of “no” tokens.
     * @return yesReward  Raw share of loser pool if “yes” wins (before fees).
     * @return noReward   Raw share of loser pool if “no” wins (before fees).
     */
    function getUserEstimates(address _user, address _prediction)
        external
        view
        returns (
            uint256 yesBalance,
            uint256 noBalance,
            uint256 yesTotal,
            uint256 noTotal,
            uint256 yesReward,
            uint256 noReward,
            uint256 currentPrice
        )
    {
        Prediction storage p = predictions[_prediction];
        (uint256 yesToken, uint256 noToken) = hlpGet6909Ids(_prediction);
        yesBalance = balanceOf(_user, yesToken);
        noBalance = balanceOf(_user, noToken);
        yesTotal = totalSupply(yesToken);
        noTotal = totalSupply(noToken);

        uint256 estimateFee;
        currentPrice = p.resolvedPrice;
        if (currentPrice > 0) {
            // if we here then prediction already resolved and we can
            // show reward to user
            if (yesTotal > 0) {
                yesReward = noTotal * (yesBalance * SCALE / yesTotal) / SCALE;
                estimateFee = (yesReward * (FEE_CREATOR_PERCENT + FEE_PROTOCOL_PERCENT) * SCALE)
                   / PERCENT_DENOMINATOR / SCALE;
                yesReward -= estimateFee;
            }

            if (noTotal > 0) {
                noReward = yesTotal * (noBalance * SCALE / noTotal) / SCALE;
                estimateFee = (noReward * (FEE_CREATOR_PERCENT + FEE_PROTOCOL_PERCENT) * SCALE) 
                   / PERCENT_DENOMINATOR / SCALE;
                noReward -= estimateFee;   
            }

        } else {
            currentPrice = _getCurrentOraclePrice(_prediction, p);
        }
            
    }

    /**
     * @notice Helper to compute ERC6909 token IDs for given prediction.
     * @param _prediction Address of prediction creator.
     * @return yesId Token ID for “yes” shares.
     * @return noId  Token ID for “no” shares.
     *
     * @dev Encoding:
     *      yesId = (uint160(creator) << 96) | 1
     *      noId  = (uint160(creator) << 96)
     */
    function hlpGet6909Ids(address _prediction) public pure returns (uint256 yesId, uint256 noId) {
        yesId = (uint256(uint160(_prediction)) << 96) | 1;
        noId = (uint256(uint160(_prediction)) << 96);
    }

    /**
     * @notice Builds a Permit2 `PermitTransferFrom` struct and the corresponding EIP-712 digest
     *         for signing an off-chain approval that allows staking via Permit2.
     *
     * @dev This helper is intended for frontend / off-chain usage.
     *      It prepares:
     *       - a Permit2-compatible permit allowing this contract to pull exactly
     *         `strike.amount` of `strike.token` from the signer
     *       - the EIP-712 digest that must be signed by the user
     *
     *      The resulting signature can later be passed to {voteWithPermit2}.
     *
     * @param _prediction Address of the prediction creator (prediction identifier).
     * @param _deadline   Unix timestamp after which the permit becomes invalid.
     *
     * @return permit  Permit2 `PermitTransferFrom` struct containing:
     *                 - token permissions (token + exact amount)
     *                 - nonce
     *                 - deadline
     *
     * @return digest  EIP-712 digest to be signed by the user for Permit2 execution.
     *
     * @custom:security
     * - The nonce is derived from `(prediction, block.timestamp)` and is intended
     *   for short-lived, UI-generated permits.
     * - Frontends MUST ensure the signature is used promptly to avoid nonce reuse.
     * - This function does NOT store any state and does NOT guarantee nonce uniqueness
     *   across blocks or chains.
     *
     * @custom:integration
     * - The returned `digest` must be signed using `eth_signTypedData_v4`.
     * - The signed permit must be submitted via `voteWithPermit2`.
     *
     * @custom:warning
     * - This helper assumes Permit2 DOMAIN_SEPARATOR is stable for the target chain.
     * - Contracts relying on deterministic nonces should override this logic.
     */
    function hlpGetPermitAndDigest(address _prediction, uint256 _deadline)
        public
        view
        returns (IPermit2Minimal.PermitTransferFrom memory permit, bytes32 digest, bytes32 hashedDigest)
    {
        Prediction storage p = predictions[_prediction];
        bytes32 DOMAIN_SEPARATOR = IPermit2Minimal(PERMIT2).DOMAIN_SEPARATOR();
        IPermit2Minimal.TokenPermissions memory tp = IPermit2Minimal.TokenPermissions(p.strike.token, p.strike.amount);
        // (uint256 yesToken, uint256 noToken) = hlpGet6909Ids(_prediction);
        uint256 nonce = uint256(keccak256(abi.encodePacked(_prediction, block.timestamp, block.chainid)));
        permit = IPermit2Minimal.PermitTransferFrom(tp, nonce, _deadline);
        bytes32 tokenPermissions = keccak256(abi.encode(_TOKEN_PERMISSIONS_TYPEHASH, tp));
        
        digest = keccak256(abi.encode(_PERMIT_TRANSFER_FROM_TYPEHASH, tokenPermissions, address(this), nonce, _deadline));

        hashedDigest = keccak256(
            abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, digest)
        );
    }

    // ==================================
    //           INTERNAL LOGIC
    // ==================================
    function _checkPrediction(address _creator, Prediction calldata _pred) internal virtual {}

    /**
     * @dev Internal implementation of prediction creation.
     *      Reverts if creator already has an active prediction.
     */
    function _createPrediction(address _creator, Prediction calldata _pred) internal virtual {
        Prediction storage p = predictions[_creator];
        if (p.expirationTime != 0) {
            revert ActivePredictionExist(_creator);
        }
        predictions[_creator] = _pred;
    }

    /**
     * @dev Internal voting logic.
     *      Mints ERC6909 vote-shares and pulls the user's ERC20 stake.
     * @param _user Voting user.
     * @param _prediction Prediction creator address.
     * @param _agree Vote type (true = yes, false = no).
     */
    function _vote(address _user, address _prediction, bool _agree) internal returns (address, uint96) {
        Prediction storage p = predictions[_prediction];
        if (p.expirationTime == 0) revert PredictionNotExist(_prediction);

        // Only allow voting before expiration
        if (p.expirationTime > block.timestamp + STOP_BEFORE_EXPIRED) {
            //CompactAsset storage s = p.strike;

            // Construct 6909 tokenId
            uint256 tokenId = (uint256(uint160(_prediction)) << 96) | (_agree ? 1 : 0);

            // Mint share tokens equal to strike amount
            _mint(_user, tokenId, p.strike.amount);
        } else {
            revert PredictionExpired(_prediction, p.expirationTime);
        }

        emit Voted(_user, _prediction, _agree);
        return (p.strike.token, p.strike.amount);
    }

    /**
     * @dev Resolve a prediction by fetching its actual price from oracle.
     *      Only executes once per prediction (idempotent).
     *
     * @return isResolved True if prediction has a non-zero resolvedPrice after the call.
     *
     * Requirements:
     * - Prediction MUST exist.
     * - Current time MUST be >= expirationTime.
     * - Oracle price MUST fit into uint96.
     */
    function _resolvePrediction(address _prediction) internal virtual returns (bool isResolved) {
        Prediction storage p = predictions[_prediction];

        if (
            p.expirationTime <= block.timestamp // time to resolve came
                && p.resolvedPrice == 0 // implicit resolved flag
        ) {
            uint256 oraclePrice = _getCurrentOraclePrice(_prediction, p);
            if (oraclePrice > type(uint96).max) {
                revert OraclePriceTooHigh(oraclePrice);
            }

            p.resolvedPrice = uint96(oraclePrice);
            emit PredictionResolved(_prediction, oraclePrice);
        }
        isResolved = p.resolvedPrice > 0;
    }

    /// @dev Handles the "no-contest" case: if either YES or NO totalSupply is zero,
    ///      user gets their stake refunded for both sides they hold, and their 6909 shares are pulled back.
    /// @return isValidGame True if both sides have non-zero totalSupply.
    function _checkIsGameValidAndReturnStakesIfNot(address _user, address _prediction)
        internal
        returns (bool isValidGame)
    {
        (uint256 yesToken, uint256 noToken) = hlpGet6909Ids(_prediction);
        isValidGame = totalSupply(yesToken) > 0 && totalSupply(noToken) > 0;
        CompactAsset storage s = predictions[_prediction].strike;

        //if we have bets only one side then no game situation
        //and users can get back their bets
        if (!isValidGame) {
            uint256 y = balanceOf(_user, yesToken);
            uint256 n = balanceOf(_user, noToken);
            // 1. Pull back ERC6909 share tokens (not burned in this version)
            _transfer(_user, address(this), yesToken, y);
            // 1. Pull back ERC6909 share tokens (not burned in this version)
            _transfer(_user, address(this), noToken, n);

            // 2. Return original stake
            IERC20(s.token).safeTransfer(_user, y);
            IERC20(s.token).safeTransfer(_user, n);
        }
    }

    /**
     * @dev Claim the reward for a winning voter.
     *      Handles:
     *        - returning the original stake
     *        - pulling back 6909 share tokens
     *        - distributing fee to creator + protocol
     *        - paying remaining reward to user
     *
     * @param _user Address of the claimant.
     * @param _prediction Address of the prediction creator.
     */
    function _claim(address _user, address _prediction) internal {
        (uint256 winTokenId, uint256 winTokenBalance,, uint256 winnerPrize) =
            _getWinnerShareAndAmount(_user, _prediction);

        CompactAsset storage s = predictions[_prediction].strike;

        if (winnerPrize > 0) {
            uint256 paid;
            uint256 fee;
            // 1. Pull back ERC6909 share tokens (not burned in this version)
            _transfer(_user, address(this), winTokenId, winTokenBalance);

            // 2. Return original stake
            IERC20(s.token).safeTransfer(_user, winTokenBalance);

            // 3. Creator fee
            fee = (winnerPrize * FEE_CREATOR_PERCENT * SCALE) / PERCENT_DENOMINATOR / SCALE;
            paid += fee;
            IERC20(s.token).safeTransfer(_prediction, fee);

            // 4. Protocol fee
            fee = (winnerPrize * FEE_PROTOCOL_PERCENT * SCALE) / PERCENT_DENOMINATOR / SCALE;
            paid += fee;
            IERC20(s.token).safeTransfer(FEE_PROTOCOL_BENEFICIARY, fee);

            // 5. User reward (winnerPrize minus all fees)
            uint256 userReward = winnerPrize - paid;
            IERC20(s.token).safeTransfer(_user, userReward);

            emit Claimed(_user, _prediction, userReward);
        }
    }

    /**
     * @dev Calculates:
     *        - tokenId of winning vote
     *        - user balance of winning tokens
     *        - user's share percentage (non-denominated)
     *        - total reward owed, proportional to losing pool
     *
     * @return winTokenId           ID of the winning ERC6909 token.
     * @return winTokenBalance      User balance of winning token.
     * @return sharesNonDenominated Fraction in PERCENT_DENOMINATOR units.
     * @return prizeAmount          Raw prize before fee splits.
     */
    function _getWinnerShareAndAmount(address _user, address _prediction)
        internal
        view
        returns (uint256 winTokenId, uint256 winTokenBalance, uint256 sharesNonDenominated, uint256 prizeAmount)
    {
        Prediction storage p = predictions[_prediction];

        // True if predictedPrice <= actual oracle result
        bool predictedTrue = p.predictedPrice.amount <= p.resolvedPrice;

        // Determine winning and losing token IDs
        winTokenId = (uint256(uint160(_prediction)) << 96) | (predictedTrue ? 1 : 0);
        uint256 loserTokenId = (uint256(uint160(_prediction)) << 96) | (!predictedTrue ? 1 : 0);

        winTokenBalance = balanceOf(_user, winTokenId);
        if (winTokenBalance == 0) {
            return (winTokenId, 0, 0, 0);
        }

        // User share = userVotes / totalVotes
        uint256 totalWin = totalSupply(winTokenId);
        sharesNonDenominated = (winTokenBalance * SCALE) / totalWin;

        // Prize = share * totalLosingPool
        uint256 totalLose = totalSupply(loserTokenId);
        prizeAmount = (totalLose * sharesNonDenominated) / SCALE;
    }

    function _getCurrentOraclePrice(address _prediction, Prediction storage p) 
        internal 
        view 
        returns (uint256 oraclePrice) 
    {
        address oracleAddress = p.predictedPrice.oracle;
        oraclePrice = IEnvelopOracle(oracleAddress).getIndexPrice(_prediction);
        if (oraclePrice == 0) {
            oraclePrice = IEnvelopOracle(oracleAddress).getIndexPrice(p.portfolio);
        }

    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC6909/extensions/ERC6909TokenSupply.sol)

pragma solidity ^0.8.20;

import {ERC6909} from "../ERC6909.sol";
import {IERC6909TokenSupply} from "../../../interfaces/IERC6909.sol";

/**
 * @dev Implementation of the Token Supply extension defined in ERC6909.
 * Tracks the total supply of each token id individually.
 */
contract ERC6909TokenSupply is ERC6909, IERC6909TokenSupply {
    mapping(uint256 id => uint256) private _totalSupplies;

    /// @inheritdoc IERC6909TokenSupply
    function totalSupply(uint256 id) public view virtual override returns (uint256) {
        return _totalSupplies[id];
    }

    /// @dev Override the `_update` function to update the total supply of each token id as necessary.
    function _update(address from, address to, uint256 id, uint256 amount) internal virtual override {
        super._update(from, to, id, amount);

        if (from == address(0)) {
            _totalSupplies[id] += amount;
        }
        if (to == address(0)) {
            unchecked {
                // amount <= _balances[from][id] <= _totalSupplies[id]
                _totalSupplies[id] -= amount;
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 *
 * IMPORTANT: Deprecated. This storage-based reentrancy guard will be removed and replaced
 * by the {ReentrancyGuardTransient} variant in v6.0.
 *
 * @custom:stateless
 */
abstract contract ReentrancyGuard {
    using StorageSlot for bytes32;

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant REENTRANCY_GUARD_STORAGE =
        0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    /**
     * @dev A `view` only version of {nonReentrant}. Use to block view functions
     * from being called, preventing reading from inconsistent contract state.
     *
     * CAUTION: This is a "view" modifier and does not change the reentrancy
     * status. Use it only on view functions. For payable or non-payable functions,
     * use the standard {nonReentrant} modifier instead.
     */
    modifier nonReentrantView() {
        _nonReentrantBeforeView();
        _;
    }

    function _nonReentrantBeforeView() private view {
        if (_reentrancyGuardEntered()) {
            revert ReentrancyGuardReentrantCall();
        }
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        _nonReentrantBeforeView();

        // Any calls to nonReentrant after this point will fail
        _reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED;
    }

    function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) {
        return REENTRANCY_GUARD_STORAGE;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.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 {
        if (!_safeTransfer(token, to, value, true)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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 {
        if (!_safeTransferFrom(token, from, to, value, true)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _safeTransfer(token, to, value, false);
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _safeTransferFrom(token, from, to, value, false);
    }

    /**
     * @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 {
        if (!_safeApprove(token, spender, value, false)) {
            if (!_safeApprove(token, spender, 0, true)) revert SafeERC20FailedOperation(address(token));
            if (!_safeApprove(token, spender, value, true)) revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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 relies 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 relies 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}.
     * Oppositely, 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 `token.transfer(to, value)` call, 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 to The recipient of the tokens
     * @param value The amount of token to transfer
     * @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
     */
    function _safeTransfer(IERC20 token, address to, uint256 value, bool bubble) private returns (bool success) {
        bytes4 selector = IERC20.transfer.selector;

        assembly ("memory-safe") {
            let fmp := mload(0x40)
            mstore(0x00, selector)
            mstore(0x04, and(to, shr(96, not(0))))
            mstore(0x24, value)
            success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
            // if call success and return is true, all is good.
            // otherwise (not success or return is not true), we need to perform further checks
            if iszero(and(success, eq(mload(0x00), 1))) {
                // if the call was a failure and bubble is enabled, bubble the error
                if and(iszero(success), bubble) {
                    returndatacopy(fmp, 0x00, returndatasize())
                    revert(fmp, returndatasize())
                }
                // if the return value is not true, then the call is only successful if:
                // - the token address has code
                // - the returndata is empty
                success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
            }
            mstore(0x40, fmp)
        }
    }

    /**
     * @dev Imitates a Solidity `token.transferFrom(from, to, value)` call, 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 from The sender of the tokens
     * @param to The recipient of the tokens
     * @param value The amount of token to transfer
     * @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
     */
    function _safeTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 value,
        bool bubble
    ) private returns (bool success) {
        bytes4 selector = IERC20.transferFrom.selector;

        assembly ("memory-safe") {
            let fmp := mload(0x40)
            mstore(0x00, selector)
            mstore(0x04, and(from, shr(96, not(0))))
            mstore(0x24, and(to, shr(96, not(0))))
            mstore(0x44, value)
            success := call(gas(), token, 0, 0x00, 0x64, 0x00, 0x20)
            // if call success and return is true, all is good.
            // otherwise (not success or return is not true), we need to perform further checks
            if iszero(and(success, eq(mload(0x00), 1))) {
                // if the call was a failure and bubble is enabled, bubble the error
                if and(iszero(success), bubble) {
                    returndatacopy(fmp, 0x00, returndatasize())
                    revert(fmp, returndatasize())
                }
                // if the return value is not true, then the call is only successful if:
                // - the token address has code
                // - the returndata is empty
                success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
            }
            mstore(0x40, fmp)
            mstore(0x60, 0)
        }
    }

    /**
     * @dev Imitates a Solidity `token.approve(spender, value)` call, 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 spender The spender of the tokens
     * @param value The amount of token to transfer
     * @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
     */
    function _safeApprove(IERC20 token, address spender, uint256 value, bool bubble) private returns (bool success) {
        bytes4 selector = IERC20.approve.selector;

        assembly ("memory-safe") {
            let fmp := mload(0x40)
            mstore(0x00, selector)
            mstore(0x04, and(spender, shr(96, not(0))))
            mstore(0x24, value)
            success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
            // if call success and return is true, all is good.
            // otherwise (not success or return is not true), we need to perform further checks
            if iszero(and(success, eq(mload(0x00), 1))) {
                // if the call was a failure and bubble is enabled, bubble the error
                if and(iszero(success), bubble) {
                    returndatacopy(fmp, 0x00, returndatasize())
                    revert(fmp, returndatasize())
                }
                // if the return value is not true, then the call is only successful if:
                // - the token address has code
                // - the returndata is empty
                success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
            }
            mstore(0x40, fmp)
        }
    }
}

// SPDX-License-Identifier: MIT
// ENVELOP(NIFTSY) protocol V2 for NFT. Onchain Oracle
pragma solidity ^0.8.28;

/// @dev Compact representation of an ERC20 asset + amount.
/// Used both for strike configuration and as portfolio items for the oracle.
struct CompactAsset {
    address token; // ERC20 token address (20 bytes)
    uint96 amount; // Amount with token decimals (12 bytes)
}

struct OracleData {
    address oracle; //Data Provider address
    uint96 amount; // Amount with token decimals (12 bytes)
}
/// @dev Oracle interface for retrieving index prices.
/// Supports:
/// - price by index address
/// - price by custom portfolio composition
interface IEnvelopOracle {
    function getIndexPrice(address _v2Index) external view returns (uint256);
    function getIndexPrice(CompactAsset[] calldata _assets) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// ENVELOP(NIFTSY) protocol V2 for NFT. Permit2 minimal
pragma solidity ^0.8.28;

bytes32 constant _TOKEN_PERMISSIONS_TYPEHASH = keccak256("TokenPermissions(address token,uint256 amount)");
bytes32 constant _PERMIT_TRANSFER_FROM_TYPEHASH = keccak256(
    "PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)"
);

/// @dev Compact representation of an ERC20 asset + amount.
/// @dev Minimal Permit2 interface for signature-based transfers
interface IPermit2Minimal {
    /// @notice The token and amount details for a transfer signed in the permit transfer signature
    struct TokenPermissions {
        // ERC20 token address
        address token;
        // the maximum amount that can be spent
        uint256 amount;
    }

    /// @notice The signed permit message for a single token transfer
    struct PermitTransferFrom {
        TokenPermissions permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice Specifies the recipient address and amount for batched transfers.
    /// @dev Recipients and amounts correspond to the index of the signed token permissions array.
    /// @dev Reverts if the requested amount is greater than the permitted signed amount.
    struct SignatureTransferDetails {
        // recipient address
        address to;
        // spender requested amount
        uint256 requestedAmount;
    }

    /// @notice Transfers a token using a signed permit message
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
    /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
    /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
    /// @dev It returns a uint256 bitmap
    /// @dev The index, or wordPosition is capped at type(uint248).max
    function nonceBitmap(address, uint256) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC6909/ERC6909.sol)

pragma solidity ^0.8.20;

import {IERC6909} from "../../interfaces/IERC6909.sol";
import {Context} from "../../utils/Context.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";

/**
 * @dev Implementation of ERC-6909.
 * See https://eips.ethereum.org/EIPS/eip-6909
 */
contract ERC6909 is Context, ERC165, IERC6909 {
    mapping(address owner => mapping(uint256 id => uint256)) private _balances;

    mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;

    mapping(address owner => mapping(address spender => mapping(uint256 id => uint256))) private _allowances;

    error ERC6909InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 id);
    error ERC6909InsufficientAllowance(address spender, uint256 allowance, uint256 needed, uint256 id);
    error ERC6909InvalidApprover(address approver);
    error ERC6909InvalidReceiver(address receiver);
    error ERC6909InvalidSender(address sender);
    error ERC6909InvalidSpender(address spender);

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IERC6909).interfaceId || super.supportsInterface(interfaceId);
    }

    /// @inheritdoc IERC6909
    function balanceOf(address owner, uint256 id) public view virtual override returns (uint256) {
        return _balances[owner][id];
    }

    /// @inheritdoc IERC6909
    function allowance(address owner, address spender, uint256 id) public view virtual override returns (uint256) {
        return _allowances[owner][spender][id];
    }

    /// @inheritdoc IERC6909
    function isOperator(address owner, address spender) public view virtual override returns (bool) {
        return _operatorApprovals[owner][spender];
    }

    /// @inheritdoc IERC6909
    function approve(address spender, uint256 id, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, id, amount);
        return true;
    }

    /// @inheritdoc IERC6909
    function setOperator(address spender, bool approved) public virtual override returns (bool) {
        _setOperator(_msgSender(), spender, approved);
        return true;
    }

    /// @inheritdoc IERC6909
    function transfer(address receiver, uint256 id, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), receiver, id, amount);
        return true;
    }

    /// @inheritdoc IERC6909
    function transferFrom(
        address sender,
        address receiver,
        uint256 id,
        uint256 amount
    ) public virtual override returns (bool) {
        address caller = _msgSender();
        if (sender != caller && !isOperator(sender, caller)) {
            _spendAllowance(sender, caller, id, amount);
        }
        _transfer(sender, receiver, id, amount);
        return true;
    }

    /**
     * @dev Creates `amount` of token `id` and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address to, uint256 id, uint256 amount) internal {
        if (to == address(0)) {
            revert ERC6909InvalidReceiver(address(0));
        }
        _update(address(0), to, id, amount);
    }

    /**
     * @dev Moves `amount` of token `id` from `from` to `to` without checking for approvals. This function verifies
     * that neither the sender nor the receiver are address(0), which means it cannot mint or burn tokens.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 id, uint256 amount) internal {
        if (from == address(0)) {
            revert ERC6909InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC6909InvalidReceiver(address(0));
        }
        _update(from, to, id, amount);
    }

    /**
     * @dev Destroys a `amount` of token `id` from `account`.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address from, uint256 id, uint256 amount) internal {
        if (from == address(0)) {
            revert ERC6909InvalidSender(address(0));
        }
        _update(from, address(0), id, amount);
    }

    /**
     * @dev Transfers `amount` of token `id` from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 id, uint256 amount) internal virtual {
        address caller = _msgSender();

        if (from != address(0)) {
            uint256 fromBalance = _balances[from][id];
            if (fromBalance < amount) {
                revert ERC6909InsufficientBalance(from, fromBalance, amount, id);
            }
            unchecked {
                // Overflow not possible: amount <= fromBalance.
                _balances[from][id] = fromBalance - amount;
            }
        }
        if (to != address(0)) {
            _balances[to][id] += amount;
        }

        emit Transfer(caller, from, to, id, amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner`'s `id` tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to e.g. set automatic allowances for certain
     * subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 id, uint256 amount) internal virtual {
        if (owner == address(0)) {
            revert ERC6909InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC6909InvalidSpender(address(0));
        }
        _allowances[owner][spender][id] = amount;
        emit Approval(owner, spender, id, amount);
    }

    /**
     * @dev Approve `spender` to operate on all of `owner`'s tokens
     *
     * This internal function is equivalent to `setOperator`, and can be used to e.g. set automatic allowances for
     * certain subsystems, etc.
     *
     * Emits an {OperatorSet} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _setOperator(address owner, address spender, bool approved) internal virtual {
        if (owner == address(0)) {
            revert ERC6909InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC6909InvalidSpender(address(0));
        }
        _operatorApprovals[owner][spender] = approved;
        emit OperatorSet(owner, spender, approved);
    }

    /**
     * @dev Updates `owner`'s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 id, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender, id);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < amount) {
                revert ERC6909InsufficientAllowance(spender, currentAllowance, amount, id);
            }
            unchecked {
                _allowances[owner][spender][id] = currentAllowance - amount;
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (interfaces/IERC6909.sol)

pragma solidity >=0.6.2;

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

/**
 * @dev Required interface of an ERC-6909 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-6909[ERC].
 */
interface IERC6909 is IERC165 {
    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set for a token of type `id`.
     * The new allowance is `amount`.
     */
    event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount);

    /**
     * @dev Emitted when `owner` grants or revokes operator status for a `spender`.
     */
    event OperatorSet(address indexed owner, address indexed spender, bool approved);

    /**
     * @dev Emitted when `amount` tokens of type `id` are moved from `sender` to `receiver` initiated by `caller`.
     */
    event Transfer(
        address caller,
        address indexed sender,
        address indexed receiver,
        uint256 indexed id,
        uint256 amount
    );

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

    /**
     * @dev Returns the amount of tokens of type `id` that `spender` is allowed to spend on behalf of `owner`.
     *
     * NOTE: Does not include operator allowances.
     */
    function allowance(address owner, address spender, uint256 id) external view returns (uint256);

    /**
     * @dev Returns true if `spender` is set as an operator for `owner`.
     */
    function isOperator(address owner, address spender) external view returns (bool);

    /**
     * @dev Sets an approval to `spender` for `amount` of tokens of type `id` from the caller's tokens. An `amount` of
     * `type(uint256).max` signifies an unlimited approval.
     *
     * Must return true.
     */
    function approve(address spender, uint256 id, uint256 amount) external returns (bool);

    /**
     * @dev Grants or revokes unlimited transfer permission of any token id to `spender` for the caller's tokens.
     *
     * Must return true.
     */
    function setOperator(address spender, bool approved) external returns (bool);

    /**
     * @dev Transfers `amount` of token type `id` from the caller's account to `receiver`.
     *
     * Must return true.
     */
    function transfer(address receiver, uint256 id, uint256 amount) external returns (bool);

    /**
     * @dev Transfers `amount` of token type `id` from `sender` to `receiver`.
     *
     * Must return true.
     */
    function transferFrom(address sender, address receiver, uint256 id, uint256 amount) external returns (bool);
}

/**
 * @dev Optional extension of {IERC6909} that adds metadata functions.
 */
interface IERC6909Metadata is IERC6909 {
    /**
     * @dev Returns the name of the token of type `id`.
     */
    function name(uint256 id) external view returns (string memory);

    /**
     * @dev Returns the ticker symbol of the token of type `id`.
     */
    function symbol(uint256 id) external view returns (string memory);

    /**
     * @dev Returns the number of decimals for the token of type `id`.
     */
    function decimals(uint256 id) external view returns (uint8);
}

/**
 * @dev Optional extension of {IERC6909} that adds content URI functions.
 */
interface IERC6909ContentURI is IERC6909 {
    /**
     * @dev Returns URI for the contract.
     */
    function contractURI() external view returns (string memory);

    /**
     * @dev Returns the URI for the token of type `id`.
     */
    function tokenURI(uint256 id) external view returns (string memory);
}

/**
 * @dev Optional extension of {IERC6909} that adds a token supply function.
 */
interface IERC6909TokenSupply is IERC6909 {
    /**
     * @dev Returns the total supply of the token of type `id`.
     */
    function totalSupply(uint256 id) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC-1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {SlotDerivation}.
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct Int256Slot {
        int256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `Int256Slot` with member `value` located at `slot`.
     */
    function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns a `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns a `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        assembly ("memory-safe") {
            r.slot := store.slot
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

/**
 * @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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)

pragma solidity >=0.6.2;

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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)

pragma solidity >=0.4.16;

/**
 * @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 15 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)

pragma solidity >=0.4.16;

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

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

pragma solidity >=0.4.16;

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

Settings
{
  "remappings": [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "@Uopenzeppelin/=lib/openzeppelin-contracts-upgradeable/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_feeBeneficiary","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ActivePredictionExist","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ERC6909InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ERC6909InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC6909InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC6909InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC6909InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC6909InvalidSpender","type":"error"},{"inputs":[{"internalType":"uint256","name":"oraclePrice","type":"uint256"}],"name":"OraclePriceTooHigh","type":"error"},{"inputs":[{"internalType":"address","name":"prediction","type":"address"},{"internalType":"uint40","name":"expirationTime","type":"uint40"}],"name":"PredictionExpired","type":"error"},{"inputs":[{"internalType":"address","name":"prediction","type":"address"}],"name":"PredictionNotExist","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualTimestamp","type":"uint256"}],"name":"TooLongPrediction","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualLength","type":"uint256"}],"name":"TooManyPortfolioItems","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"prediction","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"OperatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"uint40","name":"expirationTime","type":"uint40"}],"name":"PredictionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prediction","type":"address"},{"indexed":false,"internalType":"uint256","name":"resolvedPrice","type":"uint256"}],"name":"PredictionResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"address","name":"prediction","type":"address"},{"indexed":false,"internalType":"bool","name":"agree","type":"bool"}],"name":"Voted","type":"event"},{"inputs":[],"name":"FEE_CREATOR_PERCENT","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_PROTOCOL_BENEFICIARY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_PROTOCOL_PERCENT","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PORTFOLIO_LEN","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PREDICTION_PERIOD","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERCENT_DENOMINATOR","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2_TTL","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SCALE","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STOP_BEFORE_EXPIRED","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_prediction","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"}],"internalType":"struct CompactAsset","name":"strike","type":"tuple"},{"components":[{"internalType":"address","name":"oracle","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"}],"internalType":"struct OracleData","name":"predictedPrice","type":"tuple"},{"internalType":"uint40","name":"expirationTime","type":"uint40"},{"internalType":"uint96","name":"resolvedPrice","type":"uint96"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"}],"internalType":"struct CompactAsset[]","name":"portfolio","type":"tuple[]"}],"internalType":"struct Predicter.Prediction","name":"_pred","type":"tuple"}],"name":"createPrediction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_prediction","type":"address"}],"name":"getUserEstimates","outputs":[{"internalType":"uint256","name":"yesBalance","type":"uint256"},{"internalType":"uint256","name":"noBalance","type":"uint256"},{"internalType":"uint256","name":"yesTotal","type":"uint256"},{"internalType":"uint256","name":"noTotal","type":"uint256"},{"internalType":"uint256","name":"yesReward","type":"uint256"},{"internalType":"uint256","name":"noReward","type":"uint256"},{"internalType":"uint256","name":"currentPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_prediction","type":"address"}],"name":"hlpGet6909Ids","outputs":[{"internalType":"uint256","name":"yesId","type":"uint256"},{"internalType":"uint256","name":"noId","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_prediction","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"hlpGetPermitAndDigest","outputs":[{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct IPermit2Minimal.TokenPermissions","name":"permitted","type":"tuple"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct IPermit2Minimal.PermitTransferFrom","name":"permit","type":"tuple"},{"internalType":"bytes32","name":"digest","type":"bytes32"},{"internalType":"bytes32","name":"hashedDigest","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"creator","type":"address"}],"name":"predictions","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"}],"internalType":"struct CompactAsset","name":"strike","type":"tuple"},{"components":[{"internalType":"address","name":"oracle","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"}],"internalType":"struct OracleData","name":"predictedPrice","type":"tuple"},{"internalType":"uint40","name":"expirationTime","type":"uint40"},{"internalType":"uint96","name":"resolvedPrice","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_prediction","type":"address"},{"internalType":"bool","name":"_agree","type":"bool"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_prediction","type":"address"},{"internalType":"bool","name":"_agree","type":"bool"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct IPermit2Minimal.TokenPermissions","name":"permitted","type":"tuple"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct IPermit2Minimal.PermitTransferFrom","name":"permit","type":"tuple"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"requestedAmount","type":"uint256"}],"internalType":"struct IPermit2Minimal.SignatureTransferDetails","name":"transfer","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"voteWithPermit2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_prediction","type":"address"},{"internalType":"bool","name":"_agree","type":"bool"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct IPermit2Minimal.TokenPermissions","name":"permitted","type":"tuple"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct IPermit2Minimal.PermitTransferFrom","name":"permit","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"voteWithPermit2","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a0604052348015600e575f5ffd5b5060405161257c38038061257c833981016040819052602b91605f565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00556001600160a01b0316608052608a565b5f60208284031215606e575f5ffd5b81516001600160a01b03811681146083575f5ffd5b9392505050565b6080516124d36100a95f395f818161028801526112f201526124d35ff3fe608060405234801561000f575f5ffd5b50600436106101ba575f3560e01c80639e6c2959116100f3578063c19b13aa11610093578063eced55261161006e578063eced55261461059b578063f8ea7eb6146105aa578063fd8caf15146105b2578063fe99049a146105bd575f5ffd5b8063c19b13aa14610578578063c6176ae81461058b578063d0c4f4c914610594575f5ffd5b8063b6363cf2116100ce578063b6363cf2146104ee578063bc74500e14610529578063bd041c4d14610546578063bd85b03914610559575f5ffd5b80639e6c2959146104b2578063a1735138146104d3578063aa84aaee146104e6575f5ffd5b8063426a84931161015e578063558a729711610139578063558a729714610437578063598af9e71461044a5780636afdd850146104895780638debc55d1461049f575f5ffd5b8063426a8493146103a5578063503815cc146103b857806350497b42146103ef575f5ffd5b80631906476d116101995780631906476d1461021a5780631e83409a1461026e5780632d3e82e61461028357806335fa61fa146102c2575f5ffd5b8062fdd58e146101be57806301ffc9a7146101e4578063095bcdb614610207575b5f5ffd5b6101d16101cc366004611cf6565b6105d0565b6040519081526020015b60405180910390f35b6101f76101f2366004611d20565b6105f8565b60405190151581526020016101db565b6101f7610215366004611d47565b61062c565b61022d610228366004611cf6565b610644565b60408051845180516001600160a01b0316825260209081015181830152850151818301529301516060840152608083019190915260a082015260c0016101db565b61028161027c366004611d79565b61086f565b005b6102aa7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101db565b6103496102d0366004611d79565b600460209081525f9182526040918290208251808401845281546001600160a01b0380821683526001600160601b03600160a01b9283900481168487015286518088019097526001850154918216875291900481169385019390935260029091015490929164ffffffffff821691600160281b90041684565b6040805185516001600160a01b0390811682526020968701516001600160601b039081168884015286519091169282019290925294909301518316606085015264ffffffffff90911660808401521660a082015260c0016101db565b6101f76103b3366004611d47565b6108b7565b6103da6103c6366004611d79565b60601b6001600160601b0319166001811791565b604080519283526020830191909152016101db565b6104026103fd366004611d94565b6108c4565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016101db565b6101f7610445366004611ddf565b610a9d565b6101d1610458366004611e12565b6001600160a01b039283165f9081526002602090815260408083209490951682529283528381209181529152205490565b6102aa6e22d473030f116ddee9f6b43ac78ba381565b6102816104ad366004611eab565b610ab2565b6104bb61271081565b6040516001600160601b0390911681526020016101db565b6102816104e1366004611f3c565b610c94565b6101d1606481565b6101f76104fc366004611d94565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205460ff1690565b6105305f81565b60405164ffffffffff90911681526020016101db565b610281610554366004611ddf565b610dcd565b6101d1610567366004611fb0565b5f9081526003602052604090205490565b610281610586366004611fc7565b610e21565b6101d161025881565b6104bb5f81565b6104bb670de0b6b3a764000081565b6104bb606481565b6105306305265c0081565b6101f76105cb366004611ffe565b610f20565b6001600160a01b0382165f908152602081815260408083208484529091529020545b92915050565b5f6001600160e01b03198216630f632fb360e01b14806105f257506301ffc9a760e01b6001600160e01b03198316146105f2565b5f61063933858585610f89565b5060015b9392505050565b6040805160a0810182525f6060820181815260808301829052825260208083018290528284018290526001600160a01b038616825260048082528483208551633644e51560e01b81529551949593948594919385936e22d473030f116ddee9f6b43ac78ba393633644e51593818301939290918290030181865afa1580156106ce573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f29190612041565b60408051808201825284546001600160a01b0381168252600160a01b90046001600160601b031660208083019190915291516001600160601b031960608c901b16928101929092524260348301524660548301529192505f90607401604051602081830303815290604052805190602001205f1c905060405180606001604052808381526020018281526020018981525096505f7f618358ac3db8dc274f0cd8829da7e234bd48cd73c4a740aede1adec9846d06a1836040516020016107b9929190612058565b60408051808303601f1901815282825280516020918201207f939c21a48a8dbe3a9a2404a1d46691e4d39f6583d6ec6b35714604c986d801068285015283830152306060840152608083019490945260a08083019b909b528051808303909b018b5260c0820181528a519a84019a909a2061190160f01b60e083015260e28201959095526101028082018690528a5180830390910181526101229091019099525087519701969096209497909650939450505050565b610877610fed565b61088081611008565b1561089e5761088f3382611114565b1561089e5761089e33826111ea565b6108b460015f51602061247e5f395f51905f5255565b50565b5f61063933858585611393565b6001600160a01b0381165f90815260046020526040812081908190819081908190819081806109018b60601b6001600160601b0319166001811791565b9150915061090f8c836105d0565b995061091b8c826105d0565b5f8381526003602052604080822054848352908220546002870154939c50909a509850600160281b9091046001600160601b031694508415610a805788156109e957670de0b6b3a764000089610971828e612093565b61097b91906120aa565b610985908a612093565b61098f91906120aa565b9650670de0b6b3a7640000612710816109a95f60646120c9565b6109bc906001600160601b03168b612093565b6109c69190612093565b6109d091906120aa565b6109da91906120aa565b90506109e681886120e8565b96505b8715610a7b57670de0b6b3a764000088610a03828d612093565b610a0d91906120aa565b610a17908b612093565b610a2191906120aa565b9550670de0b6b3a764000061271081610a3b5f60646120c9565b610a4e906001600160601b03168a612093565b610a589190612093565b610a6291906120aa565b610a6c91906120aa565b9050610a7881876120e8565b95505b610a8d565b610a8a8c85611452565b94505b5050505092959891949750929550565b5f610aa9338484611543565b50600192915050565b610aba610fed565b6001600160a01b038087165f908152600460209081526040909120805490921690610ae790870187611d79565b6001600160a01b031614610b395760405162461bcd60e51b81526020600482015260146024820152732832b936b4ba191d103bb937b733903a37b5b2b760611b60448201526064015b60405180910390fd5b30610b476020860186611d79565b6001600160a01b031614610b9d5760405162461bcd60e51b815260206004820152601860248201527f5065726d6974323a2077726f6e6720726563697069656e7400000000000000006044820152606401610b30565b8054600160a01b90046001600160601b0316602085013514610c015760405162461bcd60e51b815260206004820152601c60248201527f5065726d6974323a20696e73756666696369656e7420616d6f756e74000000006044820152606401610b30565b610c0c338888611601565b505060405163187945bd60e11b81526e22d473030f116ddee9f6b43ac78ba3906330f28b7a90610c489088908890339089908990600401612163565b5f604051808303815f87803b158015610c5f575f5ffd5b505af1158015610c71573d5f5f3e3d5ffd5b5050505050610c8c60015f51602061247e5f395f51905f5255565b505050505050565b610c9c610fed565b6001600160a01b038086165f908152600460209081526040909120805490921690610cc990860186611d79565b6001600160a01b031614610d165760405162461bcd60e51b81526020600482015260146024820152732832b936b4ba191d103bb937b733903a37b5b2b760611b6044820152606401610b30565b604080518082019091523081528154600160a01b90046001600160601b03166020820152610d45338888611601565b505060405163187945bd60e11b81526e22d473030f116ddee9f6b43ac78ba3906330f28b7a90610d81908890859033908a908a906004016121ad565b5f604051808303815f87803b158015610d98575f5ffd5b505af1158015610daa573d5f5f3e3d5ffd5b505050505050610dc660015f51602061247e5f395f51905f5255565b5050505050565b610dd5610fed565b5f5f610de2338585611601565b9092509050610e056001600160a01b03831633306001600160601b03851661176e565b5050610e1d60015f51602061247e5f395f51905f5255565b5050565b6064610e3060c08301836121d6565b90501115610e6357610e4560c08201826121d6565b60405163334e120b60e01b8152610b30925060040190815260200190565b610e71426305265c0061221c565b64ffffffffff16610e8860a083016080840161224b565b64ffffffffff161115610ec857610ea560a082016080830161224b565b604051630f355a4b60e01b815264ffffffffff9091166004820152602401610b30565b610ed233826117a4565b337f49590a953e4f7ed634e3098ee5653ebc3623584f3677075fd182a1cdf75b1160610f0460a084016080850161224b565b60405164ffffffffff909116815260200160405180910390a250565b5f336001600160a01b0386168114801590610f6057506001600160a01b038087165f9081526001602090815260408083209385168352929052205460ff16155b15610f7157610f7186828686611813565b610f7d86868686610f89565b50600195945050505050565b6001600160a01b038416610fb2576040516301486a4160e71b81525f6004820152602401610b30565b6001600160a01b038316610fdb57604051630b8bbd6160e41b81525f6004820152602401610b30565b610fe7848484846118c2565b50505050565b610ff5611927565b60025f51602061247e5f395f51905f5255565b6001600160a01b0381165f90815260046020526040812060028101544264ffffffffff9091161180159061104e57506002810154600160281b90046001600160601b0316155b156110f8575f61105e8483611452565b90506001600160601b0381111561108b5760405163117fa68b60e31b815260048101829052602401610b30565b60028201805470ffffffffffffffffffffffff00000000001916600160281b6001600160601b038416021790556040518181526001600160a01b038516907f2b3604031e8bef4a9d91a0ac0e5b51a703c8a979bf952a1bf12725215ad307319060200160405180910390a2505b60020154600160281b90046001600160601b0316151592915050565b5f5f5f61112f8460601b6001600160601b0319166001811791565b915091505f611149835f9081526003602052604090205490565b11801561116157505f81815260036020526040812054115b6001600160a01b0385165f908152600460205260409020909350836111e1575f61118b87856105d0565b90505f61119888856105d0565b90506111a688308785610f89565b6111b288308684610f89565b82546111c8906001600160a01b03168984611958565b82546111de906001600160a01b03168983611958565b50505b50505092915050565b5f5f5f6111f78585611992565b6001600160a01b0388165f908152600460205260409020939650919450909250508115610c8c575f5f61122c88308888610f89565b8254611242906001600160a01b03168987611958565b670de0b6b3a76400006127108161125a606488612093565b6112649190612093565b61126e91906120aa565b61127891906120aa565b90506112848183612266565b835490925061129d906001600160a01b03168883611958565b670de0b6b3a7640000612710816112b45f88612093565b6112be9190612093565b6112c891906120aa565b6112d291906120aa565b90506112de8183612266565b8354909250611317906001600160a01b03167f000000000000000000000000000000000000000000000000000000000000000083611958565b5f61132283866120e8565b845490915061133b906001600160a01b03168a83611958565b876001600160a01b0316896001600160a01b03167ff7a40077ff7a04c7e61f6f26fb13774259ddf1b6bce9ecf26a8276cdd39926838360405161138091815260200190565b60405180910390a3505050505050505050565b6001600160a01b0384166113bc5760405163198ecd5360e31b81525f6004820152602401610b30565b6001600160a01b0383166113e557604051636f65f46560e01b81525f6004820152602401610b30565b6001600160a01b038481165f8181526002602090815260408083209488168084529482528083208784528252918290208590559051848152859392917fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a7910160405180910390a450505050565b6001810154604051630b263e0160e41b81526001600160a01b0384811660048301525f921690819063b263e01090602401602060405180830381865afa15801561149e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114c29190612041565b9150815f0361153c57604051630fcd356960e11b81526001600160a01b03821690631f9a6ad2906114fa906003870190600401612279565b602060405180830381865afa158015611515573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115399190612041565b91505b5092915050565b6001600160a01b03831661156c5760405163198ecd5360e31b81525f6004820152602401610b30565b6001600160a01b03821661159557604051636f65f46560e01b81525f6004820152602401610b30565b6001600160a01b038381165f81815260016020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a3505050565b6001600160a01b0382165f908152600460205260408120600281015482919064ffffffffff16820361165157604051636db31fe360e11b81526001600160a01b0386166004820152602401610b30565b61165b5f42612266565b600282015464ffffffffff1611156116c2575f84611679575f61167c565b60015b60ff166060876001600160a01b0316901b1790506116bc8782845f015f0160149054906101000a90046001600160601b03166001600160601b0316611aac565b506116f9565b600281015460405163051a59dd60e21b81526001600160a01b038716600482015264ffffffffff9091166024820152604401610b30565b846001600160a01b0316866001600160a01b03167ff01e0648b75d055c003bb115baaa99af91e34a86a5405eb8b96e451bcb1c79f086604051611740911515815260200190565b60405180910390a3546001600160a01b03811696600160a01b9091046001600160601b031695509350505050565b61177c848484846001611ae1565b610fe757604051635274afe760e01b81526001600160a01b0385166004820152602401610b30565b6001600160a01b0382165f908152600460205260409020600281015464ffffffffff16156117f0576040516306cd244760e41b81526001600160a01b0384166004820152602401610b30565b6001600160a01b0383165f9081526004602052604090208290610dc682826123b2565b6001600160a01b038481165f9081526002602090815260408083209387168352928152828220858352905220545f19811015610dc6578181101561188a57604051632c51fead60e11b81526001600160a01b0385166004820152602481018290526044810183905260648101849052608401610b30565b6001600160a01b038086165f908152600260209081526040808320938816835292815282822086835290522082820390555050505050565b6118ce84848484611b4e565b6001600160a01b0384166118ff575f82815260036020526040812080548392906118f9908490612266565b90915550505b6001600160a01b038316610fe7575f8281526003602052604090208054829003905550505050565b5f51602061247e5f395f51905f525460020361195657604051633ee5aeb560e01b815260040160405180910390fd5b565b6119658383836001611c80565b61198d57604051635274afe760e01b81526001600160a01b0384166004820152602401610b30565b505050565b6001600160a01b0381165f90815260046020526040812060028101546001820154839283928392600160281b9091046001600160601b03908116600160a01b909204161115806119e2575f6119e5565b60015b60ff166060886001600160a01b0316901b1795505f8115611a06575f611a09565b60015b60ff166060896001600160a01b0316901b179050611a2789886105d0565b9550855f03611a4257505f9450849350839250611aa3915050565b5f8781526003602052604090205480611a63670de0b6b3a764000089612093565b611a6d91906120aa565b5f83815260036020526040902054909650670de0b6b3a7640000611a918883612093565b611a9b91906120aa565b955050505050505b92959194509250565b6001600160a01b038316611ad557604051630b8bbd6160e41b81525f6004820152602401610b30565b61198d5f8484846118c2565b6040516323b872dd60e01b5f8181526001600160a01b038781166004528616602452604485905291602083606481808c5af1925060015f51148316611b3d578383151615611b31573d5f823e3d81fd5b5f883b113d1516831692505b604052505f60605295945050505050565b336001600160a01b03851615611be7576001600160a01b0385165f9081526020818152604080832086845290915290205482811015611bc0576040516302c6d3fb60e61b81526001600160a01b0387166004820152602481018290526044810184905260648101859052608401610b30565b6001600160a01b0386165f9081526020818152604080832087845290915290209083900390555b6001600160a01b03841615611c2c576001600160a01b0384165f9081526020818152604080832086845290915281208054849290611c26908490612266565b90915550505b604080516001600160a01b03838116825260208201859052859281881692918916917f1b3d7edb2e9c0b0e7c525b20aaaef0f5940d2ed71663c7d39266ecafac728859910160405180910390a45050505050565b60405163a9059cbb60e01b5f8181526001600160a01b038616600452602485905291602083604481808b5af1925060015f51148316611cd6578383151615611cca573d5f823e3d81fd5b5f873b113d1516831692505b60405250949350505050565b6001600160a01b03811681146108b4575f5ffd5b5f5f60408385031215611d07575f5ffd5b8235611d1281611ce2565b946020939093013593505050565b5f60208284031215611d30575f5ffd5b81356001600160e01b03198116811461063d575f5ffd5b5f5f5f60608486031215611d59575f5ffd5b8335611d6481611ce2565b95602085013595506040909401359392505050565b5f60208284031215611d89575f5ffd5b813561063d81611ce2565b5f5f60408385031215611da5575f5ffd5b8235611db081611ce2565b91506020830135611dc081611ce2565b809150509250929050565b80358015158114611dda575f5ffd5b919050565b5f5f60408385031215611df0575f5ffd5b8235611dfb81611ce2565b9150611e0960208401611dcb565b90509250929050565b5f5f5f60608486031215611e24575f5ffd5b8335611e2f81611ce2565b92506020840135611e3f81611ce2565b929592945050506040919091013590565b5f60808284031215611e60575f5ffd5b50919050565b5f5f83601f840112611e76575f5ffd5b50813567ffffffffffffffff811115611e8d575f5ffd5b602083019150836020828501011115611ea4575f5ffd5b9250929050565b5f5f5f5f5f5f868803610120811215611ec2575f5ffd5b8735611ecd81611ce2565b9650611edb60208901611dcb565b9550611eea8960408a01611e50565b9450604060bf1982011215611efd575f5ffd5b5060c08701925061010087013567ffffffffffffffff811115611f1e575f5ffd5b611f2a89828a01611e66565b979a9699509497509295939492505050565b5f5f5f5f5f60e08688031215611f50575f5ffd5b8535611f5b81611ce2565b9450611f6960208701611dcb565b9350611f788760408801611e50565b925060c086013567ffffffffffffffff811115611f93575f5ffd5b611f9f88828901611e66565b969995985093965092949392505050565b5f60208284031215611fc0575f5ffd5b5035919050565b5f60208284031215611fd7575f5ffd5b813567ffffffffffffffff811115611fed575f5ffd5b820160e0818503121561063d575f5ffd5b5f5f5f5f60808587031215612011575f5ffd5b843561201c81611ce2565b9350602085013561202c81611ce2565b93969395505050506040820135916060013590565b5f60208284031215612051575f5ffd5b5051919050565b8281526060810161063d602083018480516001600160a01b03168252602090810151910152565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176105f2576105f261207f565b5f826120c457634e487b7160e01b5f52601260045260245ffd5b500490565b6001600160601b0381811683821601908111156105f2576105f261207f565b818103818111156105f2576105f261207f565b803561210681611ce2565b6001600160a01b03168252602090810135910152565b61212682826120fb565b60408181013590830152606090810135910152565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b61216d818761211c565b61217a60808201866120fb565b6001600160a01b03841660c082015261010060e082018190525f906121a2908301848661213b565b979650505050505050565b6121b7818761211c565b84516001600160a01b03166080820152602085015160a082015261217a565b5f5f8335601e198436030181126121eb575f5ffd5b83018035915067ffffffffffffffff821115612205575f5ffd5b6020019150600681901b3603821315611ea4575f5ffd5b64ffffffffff81811683821601908111156105f2576105f261207f565b64ffffffffff811681146108b4575f5ffd5b5f6020828403121561225b575f5ffd5b813561063d81612239565b808201808211156105f2576105f261207f565b602080825282548282018190525f848152918220906040840190835b818110156122c55783546001600160a01b038116845260a01c602084015260019384019360409093019201612295565b509095945050505050565b5f81356001600160601b03811681146105f2575f5ffd5b81356122f281611ce2565b81546001600160a01b03199081166001600160a01b0392909216918217835561231d602085016122d0565b60a01b1617905550565b6801000000000000000083111561234c57634e487b7160e01b5f52604160045260245ffd5b805483825580841015612381575f828152602090208481019082015b8082101561237e575f8255600182019150612368565b50505b505f8181526020812083915b85811015610c8c5761239f83836122e7565b604092909201916001918201910161238d565b6123bc82826122e7565b6123cc60408301600183016122e7565b6002810160808301356123de81612239565b815470ffffffffffffffffffffffff00000000006123fe60a087016122d0565b60281b1664ffffffffff831670ffffffffffffffffffffffffffffffffff198316171783555050505f5f60c0840135601e1985360301811261243e575f5ffd5b84018035915067ffffffffffffffff821115612458575f5ffd5b6020019150600681901b360382131561246f575f5ffd5b610fe781836003860161232756fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a264697066735822122046427da9da2d5a5b85f1aac3d4dfd4c80900329ca297ce3fa9eff53841e85a5064736f6c634300081e003300000000000000000000000013b9cbcb46ad79878af8c9faa835bee19b977d3d

Deployed Bytecode

0x608060405234801561000f575f5ffd5b50600436106101ba575f3560e01c80639e6c2959116100f3578063c19b13aa11610093578063eced55261161006e578063eced55261461059b578063f8ea7eb6146105aa578063fd8caf15146105b2578063fe99049a146105bd575f5ffd5b8063c19b13aa14610578578063c6176ae81461058b578063d0c4f4c914610594575f5ffd5b8063b6363cf2116100ce578063b6363cf2146104ee578063bc74500e14610529578063bd041c4d14610546578063bd85b03914610559575f5ffd5b80639e6c2959146104b2578063a1735138146104d3578063aa84aaee146104e6575f5ffd5b8063426a84931161015e578063558a729711610139578063558a729714610437578063598af9e71461044a5780636afdd850146104895780638debc55d1461049f575f5ffd5b8063426a8493146103a5578063503815cc146103b857806350497b42146103ef575f5ffd5b80631906476d116101995780631906476d1461021a5780631e83409a1461026e5780632d3e82e61461028357806335fa61fa146102c2575f5ffd5b8062fdd58e146101be57806301ffc9a7146101e4578063095bcdb614610207575b5f5ffd5b6101d16101cc366004611cf6565b6105d0565b6040519081526020015b60405180910390f35b6101f76101f2366004611d20565b6105f8565b60405190151581526020016101db565b6101f7610215366004611d47565b61062c565b61022d610228366004611cf6565b610644565b60408051845180516001600160a01b0316825260209081015181830152850151818301529301516060840152608083019190915260a082015260c0016101db565b61028161027c366004611d79565b61086f565b005b6102aa7f00000000000000000000000013b9cbcb46ad79878af8c9faa835bee19b977d3d81565b6040516001600160a01b0390911681526020016101db565b6103496102d0366004611d79565b600460209081525f9182526040918290208251808401845281546001600160a01b0380821683526001600160601b03600160a01b9283900481168487015286518088019097526001850154918216875291900481169385019390935260029091015490929164ffffffffff821691600160281b90041684565b6040805185516001600160a01b0390811682526020968701516001600160601b039081168884015286519091169282019290925294909301518316606085015264ffffffffff90911660808401521660a082015260c0016101db565b6101f76103b3366004611d47565b6108b7565b6103da6103c6366004611d79565b60601b6001600160601b0319166001811791565b604080519283526020830191909152016101db565b6104026103fd366004611d94565b6108c4565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016101db565b6101f7610445366004611ddf565b610a9d565b6101d1610458366004611e12565b6001600160a01b039283165f9081526002602090815260408083209490951682529283528381209181529152205490565b6102aa6e22d473030f116ddee9f6b43ac78ba381565b6102816104ad366004611eab565b610ab2565b6104bb61271081565b6040516001600160601b0390911681526020016101db565b6102816104e1366004611f3c565b610c94565b6101d1606481565b6101f76104fc366004611d94565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205460ff1690565b6105305f81565b60405164ffffffffff90911681526020016101db565b610281610554366004611ddf565b610dcd565b6101d1610567366004611fb0565b5f9081526003602052604090205490565b610281610586366004611fc7565b610e21565b6101d161025881565b6104bb5f81565b6104bb670de0b6b3a764000081565b6104bb606481565b6105306305265c0081565b6101f76105cb366004611ffe565b610f20565b6001600160a01b0382165f908152602081815260408083208484529091529020545b92915050565b5f6001600160e01b03198216630f632fb360e01b14806105f257506301ffc9a760e01b6001600160e01b03198316146105f2565b5f61063933858585610f89565b5060015b9392505050565b6040805160a0810182525f6060820181815260808301829052825260208083018290528284018290526001600160a01b038616825260048082528483208551633644e51560e01b81529551949593948594919385936e22d473030f116ddee9f6b43ac78ba393633644e51593818301939290918290030181865afa1580156106ce573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f29190612041565b60408051808201825284546001600160a01b0381168252600160a01b90046001600160601b031660208083019190915291516001600160601b031960608c901b16928101929092524260348301524660548301529192505f90607401604051602081830303815290604052805190602001205f1c905060405180606001604052808381526020018281526020018981525096505f7f618358ac3db8dc274f0cd8829da7e234bd48cd73c4a740aede1adec9846d06a1836040516020016107b9929190612058565b60408051808303601f1901815282825280516020918201207f939c21a48a8dbe3a9a2404a1d46691e4d39f6583d6ec6b35714604c986d801068285015283830152306060840152608083019490945260a08083019b909b528051808303909b018b5260c0820181528a519a84019a909a2061190160f01b60e083015260e28201959095526101028082018690528a5180830390910181526101229091019099525087519701969096209497909650939450505050565b610877610fed565b61088081611008565b1561089e5761088f3382611114565b1561089e5761089e33826111ea565b6108b460015f51602061247e5f395f51905f5255565b50565b5f61063933858585611393565b6001600160a01b0381165f90815260046020526040812081908190819081908190819081806109018b60601b6001600160601b0319166001811791565b9150915061090f8c836105d0565b995061091b8c826105d0565b5f8381526003602052604080822054848352908220546002870154939c50909a509850600160281b9091046001600160601b031694508415610a805788156109e957670de0b6b3a764000089610971828e612093565b61097b91906120aa565b610985908a612093565b61098f91906120aa565b9650670de0b6b3a7640000612710816109a95f60646120c9565b6109bc906001600160601b03168b612093565b6109c69190612093565b6109d091906120aa565b6109da91906120aa565b90506109e681886120e8565b96505b8715610a7b57670de0b6b3a764000088610a03828d612093565b610a0d91906120aa565b610a17908b612093565b610a2191906120aa565b9550670de0b6b3a764000061271081610a3b5f60646120c9565b610a4e906001600160601b03168a612093565b610a589190612093565b610a6291906120aa565b610a6c91906120aa565b9050610a7881876120e8565b95505b610a8d565b610a8a8c85611452565b94505b5050505092959891949750929550565b5f610aa9338484611543565b50600192915050565b610aba610fed565b6001600160a01b038087165f908152600460209081526040909120805490921690610ae790870187611d79565b6001600160a01b031614610b395760405162461bcd60e51b81526020600482015260146024820152732832b936b4ba191d103bb937b733903a37b5b2b760611b60448201526064015b60405180910390fd5b30610b476020860186611d79565b6001600160a01b031614610b9d5760405162461bcd60e51b815260206004820152601860248201527f5065726d6974323a2077726f6e6720726563697069656e7400000000000000006044820152606401610b30565b8054600160a01b90046001600160601b0316602085013514610c015760405162461bcd60e51b815260206004820152601c60248201527f5065726d6974323a20696e73756666696369656e7420616d6f756e74000000006044820152606401610b30565b610c0c338888611601565b505060405163187945bd60e11b81526e22d473030f116ddee9f6b43ac78ba3906330f28b7a90610c489088908890339089908990600401612163565b5f604051808303815f87803b158015610c5f575f5ffd5b505af1158015610c71573d5f5f3e3d5ffd5b5050505050610c8c60015f51602061247e5f395f51905f5255565b505050505050565b610c9c610fed565b6001600160a01b038086165f908152600460209081526040909120805490921690610cc990860186611d79565b6001600160a01b031614610d165760405162461bcd60e51b81526020600482015260146024820152732832b936b4ba191d103bb937b733903a37b5b2b760611b6044820152606401610b30565b604080518082019091523081528154600160a01b90046001600160601b03166020820152610d45338888611601565b505060405163187945bd60e11b81526e22d473030f116ddee9f6b43ac78ba3906330f28b7a90610d81908890859033908a908a906004016121ad565b5f604051808303815f87803b158015610d98575f5ffd5b505af1158015610daa573d5f5f3e3d5ffd5b505050505050610dc660015f51602061247e5f395f51905f5255565b5050505050565b610dd5610fed565b5f5f610de2338585611601565b9092509050610e056001600160a01b03831633306001600160601b03851661176e565b5050610e1d60015f51602061247e5f395f51905f5255565b5050565b6064610e3060c08301836121d6565b90501115610e6357610e4560c08201826121d6565b60405163334e120b60e01b8152610b30925060040190815260200190565b610e71426305265c0061221c565b64ffffffffff16610e8860a083016080840161224b565b64ffffffffff161115610ec857610ea560a082016080830161224b565b604051630f355a4b60e01b815264ffffffffff9091166004820152602401610b30565b610ed233826117a4565b337f49590a953e4f7ed634e3098ee5653ebc3623584f3677075fd182a1cdf75b1160610f0460a084016080850161224b565b60405164ffffffffff909116815260200160405180910390a250565b5f336001600160a01b0386168114801590610f6057506001600160a01b038087165f9081526001602090815260408083209385168352929052205460ff16155b15610f7157610f7186828686611813565b610f7d86868686610f89565b50600195945050505050565b6001600160a01b038416610fb2576040516301486a4160e71b81525f6004820152602401610b30565b6001600160a01b038316610fdb57604051630b8bbd6160e41b81525f6004820152602401610b30565b610fe7848484846118c2565b50505050565b610ff5611927565b60025f51602061247e5f395f51905f5255565b6001600160a01b0381165f90815260046020526040812060028101544264ffffffffff9091161180159061104e57506002810154600160281b90046001600160601b0316155b156110f8575f61105e8483611452565b90506001600160601b0381111561108b5760405163117fa68b60e31b815260048101829052602401610b30565b60028201805470ffffffffffffffffffffffff00000000001916600160281b6001600160601b038416021790556040518181526001600160a01b038516907f2b3604031e8bef4a9d91a0ac0e5b51a703c8a979bf952a1bf12725215ad307319060200160405180910390a2505b60020154600160281b90046001600160601b0316151592915050565b5f5f5f61112f8460601b6001600160601b0319166001811791565b915091505f611149835f9081526003602052604090205490565b11801561116157505f81815260036020526040812054115b6001600160a01b0385165f908152600460205260409020909350836111e1575f61118b87856105d0565b90505f61119888856105d0565b90506111a688308785610f89565b6111b288308684610f89565b82546111c8906001600160a01b03168984611958565b82546111de906001600160a01b03168983611958565b50505b50505092915050565b5f5f5f6111f78585611992565b6001600160a01b0388165f908152600460205260409020939650919450909250508115610c8c575f5f61122c88308888610f89565b8254611242906001600160a01b03168987611958565b670de0b6b3a76400006127108161125a606488612093565b6112649190612093565b61126e91906120aa565b61127891906120aa565b90506112848183612266565b835490925061129d906001600160a01b03168883611958565b670de0b6b3a7640000612710816112b45f88612093565b6112be9190612093565b6112c891906120aa565b6112d291906120aa565b90506112de8183612266565b8354909250611317906001600160a01b03167f00000000000000000000000013b9cbcb46ad79878af8c9faa835bee19b977d3d83611958565b5f61132283866120e8565b845490915061133b906001600160a01b03168a83611958565b876001600160a01b0316896001600160a01b03167ff7a40077ff7a04c7e61f6f26fb13774259ddf1b6bce9ecf26a8276cdd39926838360405161138091815260200190565b60405180910390a3505050505050505050565b6001600160a01b0384166113bc5760405163198ecd5360e31b81525f6004820152602401610b30565b6001600160a01b0383166113e557604051636f65f46560e01b81525f6004820152602401610b30565b6001600160a01b038481165f8181526002602090815260408083209488168084529482528083208784528252918290208590559051848152859392917fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a7910160405180910390a450505050565b6001810154604051630b263e0160e41b81526001600160a01b0384811660048301525f921690819063b263e01090602401602060405180830381865afa15801561149e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114c29190612041565b9150815f0361153c57604051630fcd356960e11b81526001600160a01b03821690631f9a6ad2906114fa906003870190600401612279565b602060405180830381865afa158015611515573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115399190612041565b91505b5092915050565b6001600160a01b03831661156c5760405163198ecd5360e31b81525f6004820152602401610b30565b6001600160a01b03821661159557604051636f65f46560e01b81525f6004820152602401610b30565b6001600160a01b038381165f81815260016020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a3505050565b6001600160a01b0382165f908152600460205260408120600281015482919064ffffffffff16820361165157604051636db31fe360e11b81526001600160a01b0386166004820152602401610b30565b61165b5f42612266565b600282015464ffffffffff1611156116c2575f84611679575f61167c565b60015b60ff166060876001600160a01b0316901b1790506116bc8782845f015f0160149054906101000a90046001600160601b03166001600160601b0316611aac565b506116f9565b600281015460405163051a59dd60e21b81526001600160a01b038716600482015264ffffffffff9091166024820152604401610b30565b846001600160a01b0316866001600160a01b03167ff01e0648b75d055c003bb115baaa99af91e34a86a5405eb8b96e451bcb1c79f086604051611740911515815260200190565b60405180910390a3546001600160a01b03811696600160a01b9091046001600160601b031695509350505050565b61177c848484846001611ae1565b610fe757604051635274afe760e01b81526001600160a01b0385166004820152602401610b30565b6001600160a01b0382165f908152600460205260409020600281015464ffffffffff16156117f0576040516306cd244760e41b81526001600160a01b0384166004820152602401610b30565b6001600160a01b0383165f9081526004602052604090208290610dc682826123b2565b6001600160a01b038481165f9081526002602090815260408083209387168352928152828220858352905220545f19811015610dc6578181101561188a57604051632c51fead60e11b81526001600160a01b0385166004820152602481018290526044810183905260648101849052608401610b30565b6001600160a01b038086165f908152600260209081526040808320938816835292815282822086835290522082820390555050505050565b6118ce84848484611b4e565b6001600160a01b0384166118ff575f82815260036020526040812080548392906118f9908490612266565b90915550505b6001600160a01b038316610fe7575f8281526003602052604090208054829003905550505050565b5f51602061247e5f395f51905f525460020361195657604051633ee5aeb560e01b815260040160405180910390fd5b565b6119658383836001611c80565b61198d57604051635274afe760e01b81526001600160a01b0384166004820152602401610b30565b505050565b6001600160a01b0381165f90815260046020526040812060028101546001820154839283928392600160281b9091046001600160601b03908116600160a01b909204161115806119e2575f6119e5565b60015b60ff166060886001600160a01b0316901b1795505f8115611a06575f611a09565b60015b60ff166060896001600160a01b0316901b179050611a2789886105d0565b9550855f03611a4257505f9450849350839250611aa3915050565b5f8781526003602052604090205480611a63670de0b6b3a764000089612093565b611a6d91906120aa565b5f83815260036020526040902054909650670de0b6b3a7640000611a918883612093565b611a9b91906120aa565b955050505050505b92959194509250565b6001600160a01b038316611ad557604051630b8bbd6160e41b81525f6004820152602401610b30565b61198d5f8484846118c2565b6040516323b872dd60e01b5f8181526001600160a01b038781166004528616602452604485905291602083606481808c5af1925060015f51148316611b3d578383151615611b31573d5f823e3d81fd5b5f883b113d1516831692505b604052505f60605295945050505050565b336001600160a01b03851615611be7576001600160a01b0385165f9081526020818152604080832086845290915290205482811015611bc0576040516302c6d3fb60e61b81526001600160a01b0387166004820152602481018290526044810184905260648101859052608401610b30565b6001600160a01b0386165f9081526020818152604080832087845290915290209083900390555b6001600160a01b03841615611c2c576001600160a01b0384165f9081526020818152604080832086845290915281208054849290611c26908490612266565b90915550505b604080516001600160a01b03838116825260208201859052859281881692918916917f1b3d7edb2e9c0b0e7c525b20aaaef0f5940d2ed71663c7d39266ecafac728859910160405180910390a45050505050565b60405163a9059cbb60e01b5f8181526001600160a01b038616600452602485905291602083604481808b5af1925060015f51148316611cd6578383151615611cca573d5f823e3d81fd5b5f873b113d1516831692505b60405250949350505050565b6001600160a01b03811681146108b4575f5ffd5b5f5f60408385031215611d07575f5ffd5b8235611d1281611ce2565b946020939093013593505050565b5f60208284031215611d30575f5ffd5b81356001600160e01b03198116811461063d575f5ffd5b5f5f5f60608486031215611d59575f5ffd5b8335611d6481611ce2565b95602085013595506040909401359392505050565b5f60208284031215611d89575f5ffd5b813561063d81611ce2565b5f5f60408385031215611da5575f5ffd5b8235611db081611ce2565b91506020830135611dc081611ce2565b809150509250929050565b80358015158114611dda575f5ffd5b919050565b5f5f60408385031215611df0575f5ffd5b8235611dfb81611ce2565b9150611e0960208401611dcb565b90509250929050565b5f5f5f60608486031215611e24575f5ffd5b8335611e2f81611ce2565b92506020840135611e3f81611ce2565b929592945050506040919091013590565b5f60808284031215611e60575f5ffd5b50919050565b5f5f83601f840112611e76575f5ffd5b50813567ffffffffffffffff811115611e8d575f5ffd5b602083019150836020828501011115611ea4575f5ffd5b9250929050565b5f5f5f5f5f5f868803610120811215611ec2575f5ffd5b8735611ecd81611ce2565b9650611edb60208901611dcb565b9550611eea8960408a01611e50565b9450604060bf1982011215611efd575f5ffd5b5060c08701925061010087013567ffffffffffffffff811115611f1e575f5ffd5b611f2a89828a01611e66565b979a9699509497509295939492505050565b5f5f5f5f5f60e08688031215611f50575f5ffd5b8535611f5b81611ce2565b9450611f6960208701611dcb565b9350611f788760408801611e50565b925060c086013567ffffffffffffffff811115611f93575f5ffd5b611f9f88828901611e66565b969995985093965092949392505050565b5f60208284031215611fc0575f5ffd5b5035919050565b5f60208284031215611fd7575f5ffd5b813567ffffffffffffffff811115611fed575f5ffd5b820160e0818503121561063d575f5ffd5b5f5f5f5f60808587031215612011575f5ffd5b843561201c81611ce2565b9350602085013561202c81611ce2565b93969395505050506040820135916060013590565b5f60208284031215612051575f5ffd5b5051919050565b8281526060810161063d602083018480516001600160a01b03168252602090810151910152565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176105f2576105f261207f565b5f826120c457634e487b7160e01b5f52601260045260245ffd5b500490565b6001600160601b0381811683821601908111156105f2576105f261207f565b818103818111156105f2576105f261207f565b803561210681611ce2565b6001600160a01b03168252602090810135910152565b61212682826120fb565b60408181013590830152606090810135910152565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b61216d818761211c565b61217a60808201866120fb565b6001600160a01b03841660c082015261010060e082018190525f906121a2908301848661213b565b979650505050505050565b6121b7818761211c565b84516001600160a01b03166080820152602085015160a082015261217a565b5f5f8335601e198436030181126121eb575f5ffd5b83018035915067ffffffffffffffff821115612205575f5ffd5b6020019150600681901b3603821315611ea4575f5ffd5b64ffffffffff81811683821601908111156105f2576105f261207f565b64ffffffffff811681146108b4575f5ffd5b5f6020828403121561225b575f5ffd5b813561063d81612239565b808201808211156105f2576105f261207f565b602080825282548282018190525f848152918220906040840190835b818110156122c55783546001600160a01b038116845260a01c602084015260019384019360409093019201612295565b509095945050505050565b5f81356001600160601b03811681146105f2575f5ffd5b81356122f281611ce2565b81546001600160a01b03199081166001600160a01b0392909216918217835561231d602085016122d0565b60a01b1617905550565b6801000000000000000083111561234c57634e487b7160e01b5f52604160045260245ffd5b805483825580841015612381575f828152602090208481019082015b8082101561237e575f8255600182019150612368565b50505b505f8181526020812083915b85811015610c8c5761239f83836122e7565b604092909201916001918201910161238d565b6123bc82826122e7565b6123cc60408301600183016122e7565b6002810160808301356123de81612239565b815470ffffffffffffffffffffffff00000000006123fe60a087016122d0565b60281b1664ffffffffff831670ffffffffffffffffffffffffffffffffff198316171783555050505f5f60c0840135601e1985360301811261243e575f5ffd5b84018035915067ffffffffffffffff821115612458575f5ffd5b6020019150600681901b360382131561246f575f5ffd5b610fe781836003860161232756fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a264697066735822122046427da9da2d5a5b85f1aac3d4dfd4c80900329ca297ce3fa9eff53841e85a5064736f6c634300081e0033

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

00000000000000000000000013b9cbcb46ad79878af8c9faa835bee19b977d3d

-----Decoded View---------------
Arg [0] : _feeBeneficiary (address): 0x13B9cBcB46aD79878af8c9faa835Bee19B977D3D

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000013b9cbcb46ad79878af8c9faa835bee19b977d3d


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.