账户
0x5f...6aec
0x5f...6aEc

0x5f...6aEc

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.20+commit.a1b79de6
语言
Solidity
合同源代码
文件 1 的 16:Access.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract Access is AccessControl {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    constructor() {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(ADMIN_ROLE, msg.sender);
    }

    function transferOwnership(address newOwner) public onlyRole(DEFAULT_ADMIN_ROLE) {
        require(newOwner != address(0), "New owner cannot be zero address");

        _revokeRole(DEFAULT_ADMIN_ROLE, msg.sender);

        _grantRole(DEFAULT_ADMIN_ROLE, newOwner);
    }

    function addAdmin(address account) public onlyRole(DEFAULT_ADMIN_ROLE) {
        grantRole(ADMIN_ROLE, account);
    }

    function removeAdmin(address account) public onlyRole(DEFAULT_ADMIN_ROLE) {
        revokeRole(ADMIN_ROLE, account);
    }

    function isAdmin(address account) external view returns (bool) {
        return hasRole(ADMIN_ROLE, account);
    }

    function isOwner(address account) external view returns (bool) {
        return hasRole(DEFAULT_ADMIN_ROLE, account);
    }
}
合同源代码
文件 2 的 16:AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

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

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

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

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

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

        _revokeRole(role, callerConfirmation);
    }

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

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
合同源代码
文件 3 的 16:Context.sol
// 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;
    }
}
合同源代码
文件 4 的 16:ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
合同源代码
文件 5 的 16:IAccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

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

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

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

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}
合同源代码
文件 6 的 16:IConditionalTokens.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

interface IConditionalTokens is IERC1155 {
    /// Mapping key is an condition ID. Value represents numerators of the payout vector associated with the condition. This array is initialized with a length equal to the outcome slot count. E.g. Condition with 3 outcomes [A, B, C] and two of those correct [0.5, 0.5, 0]. In Ethereum there are no decimal values, so here, 0.5 is represented by fractions like 1/2 == 0.5. That's why we need numerator and denominator values. Payout numerators are also used as a check of initialization. If the numerators array is empty (has length zero), the condition was not created/prepared. See getOutcomeSlotCount.
    function payoutNumerators(bytes32) external returns (uint256[] memory);

    /// Denominator is also used for checking if the condition has been resolved. If the denominator is non-zero, then the condition has been resolved.
    function payoutDenominator(bytes32) external returns (uint256);

    /// @dev This function prepares a condition by initializing a payout vector associated with the condition.
    /// @param oracle The account assigned to report the result for the prepared condition.
    /// @param questionId An identifier for the question to be answered by the oracle.
    /// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
    function prepareCondition(address oracle, bytes32 questionId, uint256 outcomeSlotCount) external;

    /// @dev Called by the oracle for reporting results of conditions. Will set the payout vector for the condition with the ID ``keccak256(abi.encodePacked(oracle, questionId, outcomeSlotCount))``, where oracle is the message sender, questionId is one of the parameters of this function, and outcomeSlotCount is the length of the payouts parameter, which contains the payoutNumerators for each outcome slot of the condition.
    /// @param questionId The question ID the oracle is answering for
    /// @param payouts The oracle's answer
    function reportPayouts(bytes32 questionId, uint256[] calldata payouts) external;

    /// @dev This function splits a position. If splitting from the collateral, this contract will attempt to transfer `amount` collateral from the message sender to itself. Otherwise, this contract will burn `amount` stake held by the message sender in the position being split worth of EIP 1155 tokens. Regardless, if successful, `amount` stake will be minted in the split target positions. If any of the transfers, mints, or burns fail, the transaction will revert. The transaction will also revert if the given partition is trivial, invalid, or refers to more slots than the condition is prepared with.
    /// @param collateralToken The address of the positions' backing collateral token.
    /// @param parentCollectionId The ID of the outcome collections common to the position being split and the split target positions. May be null, in which only the collateral is shared.
    /// @param conditionId The ID of the condition to split on.
    /// @param partition An array of disjoint index sets representing a nontrivial partition of the outcome slots of the given condition. E.g. A|B and C but not A|B and B|C (is not disjoint). Each element's a number which, together with the condition, represents the outcome collection. E.g. 0b110 is A|B, 0b010 is B, etc.
    /// @param amount The amount of collateral or stake to split.
    function splitPosition(
        IERC20 collateralToken,
        bytes32 parentCollectionId,
        bytes32 conditionId,
        uint256[] calldata partition,
        uint256 amount
    ) external;

    function mergePositions(
        IERC20 collateralToken,
        bytes32 parentCollectionId,
        bytes32 conditionId,
        uint256[] calldata partition,
        uint256 amount
    ) external;

    function redeemPositions(
        IERC20 collateralToken,
        bytes32 parentCollectionId,
        bytes32 conditionId,
        uint256[] calldata indexSets
    ) external;

    /// @dev Gets the outcome slot count of a condition.
    /// @param conditionId ID of the condition.
    /// @return Number of outcome slots associated with a condition, or zero if condition has not been prepared yet.
    function getOutcomeSlotCount(bytes32 conditionId) external view returns (uint256);

    /// @dev Constructs a condition ID from an oracle, a question ID, and the outcome slot count for the question.
    /// @param oracle The account assigned to report the result for the prepared condition.
    /// @param questionId An identifier for the question to be answered by the oracle.
    /// @param outcomeSlotCount The number of outcome slots which should be used for this condition. Must not exceed 256.
    function getConditionId(address oracle, bytes32 questionId, uint256 outcomeSlotCount)
        external
        pure
        returns (bytes32);

    /// @dev Constructs an outcome collection ID from a parent collection and an outcome collection.
    /// @param parentCollectionId Collection ID of the parent outcome collection, or bytes32(0) if there's no parent.
    /// @param conditionId Condition ID of the outcome collection to combine with the parent outcome collection.
    /// @param indexSet Index set of the outcome collection to combine with the parent outcome collection.
    function getCollectionId(bytes32 parentCollectionId, bytes32 conditionId, uint256 indexSet)
        external
        view
        returns (bytes32);

    /// @dev Constructs a position ID from a collateral token and an outcome collection. These IDs are used as the ERC-1155 ID for this contract.
    /// @param collateralToken Collateral token which backs the position.
    /// @param collectionId ID of the outcome collection associated with this position.
    function getPositionId(IERC20 collateralToken, bytes32 collectionId) external pure returns (uint256);
}
合同源代码
文件 7 的 16:IERC1155.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Required interface of an ERC-1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[ERC].
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` amount of tokens of type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(
        address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values
    );

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

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

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the zero address.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers a `value` amount of tokens of type `id` from `from` to `to`.
     *
     * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
     * to an untrusted contract, when invoking {onERC1155Received} on the receiver.
     * Ensure to follow the checks-effects-interactions pattern and consider employing
     * reentrancy guards when interacting with untrusted contracts.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `value` amount.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * WARNING: This function can potentially allow a reentrancy attack when transferring tokens
     * to an untrusted contract, when invoking {onERC1155BatchReceived} on the receiver.
     * Ensure to follow the checks-effects-interactions pattern and consider employing
     * reentrancy guards when interacting with untrusted contracts.
     *
     * Emits either a {TransferSingle} or a {TransferBatch} event, depending on the length of the array arguments.
     *
     * Requirements:
     *
     * - `ids` and `values` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external;
}
合同源代码
文件 8 的 16:IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
合同源代码
文件 9 的 16:IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
合同源代码
文件 10 的 16:ILimitlessPriceOracleV1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

struct QuestionData {
    uint256 requestTimestamp;
    int64 price;
    int32 expo;
    uint256 confAge;
    uint256 deadline;
    uint256 emergencyResolutionTimestamp;
    bytes32 priceFeedId;
    bool initialized;
    bool resolved;
    address creator;
    int16 winningIndex;
    int64 settledPrice;
    int32 settledExpo;
    uint256 publishTime;
}

interface ILimitlessPriceOracleV1EE {
    error NotInitialized();
    error AlreadyInitialized();
    error NotPaused();
    error Paused();
    error NotFlagged();
    error NotReadyToResolve();
    error Resolved();
    error Flagged();
    error DeadlineNotPassed();
    error SafetyPeriodPassed();
    error SafetyPeriodNotPassed();
    error PriceOutdated();
    error PriceNotAvailable();
    error InvalidExpo();
    error InvalidPrice();
    error InvalidPayoutsPrice();
    error InvalidPayouts();
    error InvalidDeadline();
    error InvalidPriceFeed();

    event QuestionInitialized(
        bytes32 indexed questionId,
        address indexed creator,
        bytes32 indexed priceFeedId,
        uint256 requestTimestamp,
        int64 price,
        int32 expo,
        uint256 confTime,
        uint256 deadline
    );

    event QuestionFlagged(bytes32 indexed questionId);
    event QuestionUnflagged(bytes32 indexed questionId);
    event QuestionReset(bytes32 indexed questionId);
    event QuestionResolved(
        bytes32 indexed questionId, int256 indexed settledPrice, int32 expo, uint256[] payouts, uint256 publishTime
    );
    event QuestionEmergencyResolved(bytes32 indexed questionId, uint256[] payouts);
}

interface ILimitlessPriceOracleV1 {
    function initialize(
        bytes32 questionID,
        bytes32 priceFeedID,
        int64 price,
        int32 expo,
        uint256 confAge,
        uint256 deadline
    ) external;
}
合同源代码
文件 11 的 16:IPyth.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

    /// @notice Returns true if a price feed with the given id exists.
    /// @param id The Pyth Price Feed ID of which to check its existence.
    function priceFeedExists(bytes32 id) external view returns (bool exists);
}
合同源代码
文件 12 的 16:IPythEvents.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

/// @title IPythEvents contains the events that Pyth contract emits.
/// @dev This interface can be used for listening to the updates for off-chain and testing purposes.
interface IPythEvents {
    /// @dev Emitted when the price feed with `id` has received a fresh update.
    /// @param id The Pyth Price Feed ID.
    /// @param publishTime Publish time of the given price update.
    /// @param price Price of the given price update.
    /// @param conf Confidence interval of the given price update.
    event PriceFeedUpdate(bytes32 indexed id, uint64 publishTime, int64 price, uint64 conf);
}
合同源代码
文件 13 的 16:LimitlessPriceOracleV1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import { IPyth } from "./interfaces/IPyth.sol";
import { PythStructs } from "./interfaces/PythStructs.sol";
import { IConditionalTokens } from "./interfaces/IConditionalTokens.sol";
import { PayoutHelperLib } from "./libraries/PayoutHelperLib.sol";
import { PythUtils } from "./libraries/PythUtils.sol";
import { QuestionData, ILimitlessPriceOracleV1EE } from "./interfaces/ILimitlessPriceOracleV1.sol";
import { Access } from "./mixins/Access.sol";

/**
 * @title LimitlessPriceOracleV1
 * @dev A decentralized price oracle system that uses Pyth Network for price feeds and integrates
 * with the Conditional Tokens Framework for resolving prediction markets. This contract allows
 * for creating, managing, and resolving price-based questions with built-in emergency resolution
 * mechanisms and administrative controls.
 *
 * Key Features:
 * - Integration with Pyth Network for reliable price data
 * - Support for emergency resolution with safety periods
 * - Administrative controls for market management
 * - Flexible price comparison mechanisms with expo handling
 */
contract LimitlessPriceOracleV1 is ILimitlessPriceOracleV1EE, Access {
    /// @notice Reference to the Conditional Tokens Framework contract
    IConditionalTokens public immutable ctf;

    /// @notice Reference to the Pyth Network price feed contract
    IPyth public immutable pyth;

    /// @notice Time period required to wait before emergency resolution can be executed
    uint256 public constant EMERGENCY_SAFETY_PERIOD = 2 days;

    /// @notice Stores all question data indexed by questionID
    mapping(bytes32 => QuestionData) public questions;

    /**
     * @dev Constructor initializes the contract with required dependencies
     * @param _ctf Address of the Conditional Tokens Framework contract
     * @param _pyth Address of the Pyth Network contract
     */
    constructor(address _ctf, address _pyth) {
        ctf = IConditionalTokens(_ctf);
        pyth = IPyth(_pyth);
    }

    /*///////////////////////////////////////////////////////////////////
                            PUBLIC FUNCTIONS
    //////////////////////////////////////////////////////////////////*/

    /**
     * @notice Initializes a new price-based question
     * @param questionID Unique identifier for the question
     * @param priceFeedID Pyth price feed identifier
     * @param price Target price for comparison
     * @param expo Price exponent
     * @param confAge Confidence interval for price validation
     * @param deadline Timestamp after which the question can be resolved
     */
    function initialize(
        bytes32 questionID,
        bytes32 priceFeedID,
        int64 price,
        int32 expo,
        uint256 confAge,
        uint256 deadline
    ) external {
        if (questions[questionID].initialized) revert AlreadyInitialized();
        if (!pyth.priceFeedExists(priceFeedID)) revert InvalidPriceFeed();
        if (price <= 0) revert InvalidPrice();
        if (expo > 0) revert InvalidExpo();
        if (deadline <= block.timestamp) revert InvalidDeadline();

        _saveQuestion(questionID, block.timestamp, price, expo, confAge, deadline, priceFeedID, msg.sender);

        emit QuestionInitialized(questionID, msg.sender, priceFeedID, block.timestamp, price, expo, confAge, deadline);
    }

    /**
     * @notice Resolves a question based on the current price from Pyth
     * @param questionID Identifier of the question to resolve
     */
    function resolve(bytes32 questionID) external {
        QuestionData storage questionData = questions[questionID];
        if (!questionData.initialized) revert NotInitialized();
        if (_isFlagged(questionData)) revert Flagged();
        if (questionData.resolved) revert Resolved();
        if (block.timestamp < questionData.deadline) revert DeadlineNotPassed();

        PythStructs.Price memory oracleData =
            _fetchPrice(questionData.priceFeedId, questionData.deadline, questionData.confAge);
        _resolve(questionID, questionData, oracleData);
    }

    /**
     * @notice Parse updateData to return the first updated prices if the prices are published within the given time range.
     * @param questionID Identifier of the question to resolve
     * @param updateData Data to update the Pyth price feed
     */
    function parseFeedUniqueAndResolve(bytes32 questionID, bytes[] calldata updateData) external payable {
        QuestionData storage questionData = questions[questionID];
        if (!questionData.initialized) revert NotInitialized();
        if (_isFlagged(questionData)) revert Flagged();
        if (questionData.resolved) revert Resolved();
        if (block.timestamp < questionData.deadline) revert DeadlineNotPassed();

        uint256 updateFee = pyth.getUpdateFee(updateData);

        bytes32[] memory priceFeedIds = new bytes32[](1);
        priceFeedIds[0] = questionData.priceFeedId;

        uint64 minPublishTime = uint64(questionData.deadline - questionData.confAge);
        uint64 maxPublishTime = uint64(questionData.deadline);
        PythStructs.PriceFeed[] memory priceFeeds = pyth.parsePriceFeedUpdatesUnique{ value: updateFee }(
            updateData, priceFeedIds, minPublishTime, maxPublishTime
        );

        if (priceFeeds[0].id != questionData.priceFeedId) revert InvalidPriceFeed();

        PythStructs.Price memory oracleData = priceFeeds[0].price;

        if (oracleData.publishTime < minPublishTime) revert PriceOutdated();
        if (oracleData.price <= 0) revert InvalidPrice();
        if (oracleData.publishTime > maxPublishTime) revert PriceOutdated();

        _resolve(questionID, questionData, oracleData);
    }

    /*///////////////////////////////////////////////////////////////////
                            ADMIN FUNCTIONS
    //////////////////////////////////////////////////////////////////*/

    /**
     * @notice Flags a question for emergency resolution
     * @param questionID Identifier of the question to flag
     */
    function flag(bytes32 questionID) external onlyRole(DEFAULT_ADMIN_ROLE) {
        QuestionData storage questionData = questions[questionID];
        if (!questionData.initialized) revert NotInitialized();
        if (_isFlagged(questionData)) revert Flagged();
        if (questionData.resolved) revert Resolved();

        questionData.emergencyResolutionTimestamp = block.timestamp + EMERGENCY_SAFETY_PERIOD;

        emit QuestionFlagged(questionID);
    }

    /**
     * @notice Removes the emergency resolution flag from a question
     * @param questionID Identifier of the question to unflag
     */
    function unflag(bytes32 questionID) external onlyRole(DEFAULT_ADMIN_ROLE) {
        QuestionData storage questionData = questions[questionID];
        if (!questionData.initialized) revert NotInitialized();
        if (!_isFlagged(questionData)) revert NotFlagged();
        if (questionData.resolved) revert Resolved();
        if (block.timestamp > questionData.emergencyResolutionTimestamp) revert SafetyPeriodPassed();

        questionData.emergencyResolutionTimestamp = 0;

        emit QuestionUnflagged(questionID);
    }

    /**
     * @notice Performs emergency resolution of a question
     * @param questionID Identifier of the question to resolve
     * @param payouts Array of payout values for the resolution
     */
    function emergencyResolve(bytes32 questionID, uint256[] calldata payouts) external onlyRole(DEFAULT_ADMIN_ROLE) {
        QuestionData storage questionData = questions[questionID];
        if (!_isValidPayoutArray(payouts)) revert InvalidPayouts();
        if (!questionData.initialized) revert NotInitialized();
        if (!_isFlagged(questionData)) revert NotFlagged();
        if (block.timestamp < questionData.emergencyResolutionTimestamp) revert SafetyPeriodNotPassed();

        questionData.resolved = true;
        ctf.reportPayouts(questionID, payouts);
        emit QuestionEmergencyResolved(questionID, payouts);
    }

    /*///////////////////////////////////////////////////////////////////
                            INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////////*/

    function _saveQuestion(
        bytes32 questionID,
        uint256 requestTimestamp,
        int64 price,
        int32 expo,
        uint256 confAge,
        uint256 deadline,
        bytes32 priceFeedID,
        address creator
    ) internal {
        questions[questionID] = QuestionData({
            requestTimestamp: requestTimestamp,
            price: price,
            expo: expo,
            confAge: confAge,
            deadline: deadline,
            emergencyResolutionTimestamp: 0,
            priceFeedId: priceFeedID,
            resolved: false,
            creator: creator,
            initialized: true,
            winningIndex: -1,
            settledPrice: 0,
            settledExpo: 0,
            publishTime: 0
        });
    }

    function _fetchPrice(bytes32 priceFeedId, uint256 timestamp, uint256 confAge)
        internal
        view
        returns (PythStructs.Price memory)
    {
        uint256 age = timestamp - confAge;

        if (priceFeedId != bytes32(0)) {
            PythStructs.Price memory oracleData = pyth.getPriceNoOlderThan(priceFeedId, age);
            if (oracleData.price <= 0) revert InvalidPrice();
            if (oracleData.publishTime < age) revert PriceOutdated();
            if (oracleData.publishTime > timestamp) revert PriceOutdated();

            return oracleData;
        } else {
            revert PriceNotAvailable();
        }
    }

    function _resolvePrices(int64 qPrice, int32 qExpo, int64 oPrice, int32 oExpo) internal pure returns (int256) {
        uint256 oConvertedPrice = PythUtils.convertToUint(oPrice, oExpo, 18);
        uint256 qConvertedPrice = PythUtils.convertToUint(qPrice, qExpo, 18);

        // Valid return price 0 or 1. 0 if the oracle price is less than the question price
        return oConvertedPrice < qConvertedPrice ? int256(0) : int256(1 ether);
    }

    function _constructPayouts(int256 price) internal pure returns (uint256[] memory) {
        // Payouts: [YES, NO]
        uint256[] memory payouts = new uint256[](2);
        // Valid prices are 0 and 1
        if (price != 0 && price != 1 ether) revert InvalidPayoutsPrice();

        if (price == 0) {
            // NO: Report [Yes, No] as [0, 1]
            payouts[0] = 0;
            payouts[1] = 1;
        } else {
            // YES: Report [Yes, No] as [1, 0]
            payouts[0] = 1;
            payouts[1] = 0;
        }
        return payouts;
    }

    function _extractWinningIndex(uint256[] memory payouts) internal pure returns (int16) {
        return payouts[0] == 0 ? int16(1) : int16(0);
    }

    function _resolve(bytes32 questionID, QuestionData storage questionData, PythStructs.Price memory oracleData)
        internal
    {
        int256 resolvedPrice = _resolvePrices(questionData.price, questionData.expo, oracleData.price, oracleData.expo);

        questionData.resolved = true;

        // Construct the payout array for the question
        uint256[] memory payouts = _constructPayouts(resolvedPrice);
        if (!_isValidPayoutArray(payouts)) revert InvalidPayouts();

        // Save the resolved data to the question
        int16 winningIndex = _extractWinningIndex(payouts);
        questionData.winningIndex = winningIndex;
        questionData.settledPrice = oracleData.price;
        questionData.settledExpo = oracleData.expo;
        questionData.publishTime = oracleData.publishTime;

        // Resolve the underlying CTF market
        ctf.reportPayouts(questionID, payouts);

        emit QuestionResolved(questionID, oracleData.price, oracleData.expo, payouts, oracleData.publishTime);
    }

    function _isInitialized(QuestionData memory questionData) internal pure returns (bool) {
        return questionData.initialized;
    }

    function _isFlagged(QuestionData memory questionData) internal pure returns (bool) {
        return questionData.emergencyResolutionTimestamp > 0;
    }

    /// @notice Validates a payout array from the admin
    /// @param payouts - The payout array
    function _isValidPayoutArray(uint256[] memory payouts) internal pure returns (bool) {
        return PayoutHelperLib.isValidPayoutArray(payouts);
    }
}
合同源代码
文件 14 的 16:PayoutHelperLib.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

library PayoutHelperLib {
    function isValidPayoutArray(uint256[] memory payouts) internal pure returns (bool) {
        if (payouts.length != 2) return false;

        // Payout must be [0,1] or [1,0]
        // if payout[0] is 1, payout[1] must be 0
        if ((payouts[0] == 1) && (payouts[1] == 0)) return true;

        // If payout[0] is 0, payout[1] must be 1
        if ((payouts[0] == 0) && (payouts[1] == 1)) return true;
        return false;
    }
}
合同源代码
文件 15 的 16:PythStructs.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

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

    // PriceFeed represents a current aggregate price from pyth publisher feeds.
    struct PriceFeed {
        // The price ID.
        bytes32 id;
        // Latest available price
        Price price;
        // Latest available exponentially-weighted moving average price
        Price emaPrice;
    }
}
合同源代码
文件 16 的 16:PythUtils.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

library PythUtils {
    /// @notice Converts a Pyth price to a uint256 with a target number of decimals
    /// @param price The Pyth price
    /// @param expo The Pyth price exponent
    /// @param targetDecimals The target number of decimals
    /// @return The price as a uint256
    /// @dev Function will lose precision if targetDecimals is less than the Pyth price decimals.
    /// This method will truncate any digits that cannot be represented by the targetDecimals.
    /// e.g. If the price is 0.000123 and the targetDecimals is 2, the result will be 0
    function convertToUint(int64 price, int32 expo, uint8 targetDecimals) public pure returns (uint256) {
        if (price < 0 || expo > 0 || expo < -255) revert();

        uint8 priceDecimals = uint8(uint32(-1 * expo));

        if (targetDecimals >= priceDecimals) {
            return uint256(uint64(price)) * 10 ** uint32(targetDecimals - priceDecimals);
        } else {
            return uint256(uint64(price)) / 10 ** uint32(priceDecimals - targetDecimals);
        }
    }
}
设置
{
  "compilationTarget": {
    "src/LimitlessPriceOracleV1.sol": "LimitlessPriceOracleV1"
  },
  "evmVersion": "paris",
  "libraries": {
    "src/libraries/PayoutHelperLib.sol:PayoutHelperLib": "0xa59a6d2cf3029192dd823cf665d3145cd4dae32e",
    "src/libraries/PythUtils.sol:PythUtils": "0x13d40aacf8e6639230adfcc47020b4ea6348eea7"
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [
    ":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    ":ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    ":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/=lib/openzeppelin-contracts/"
  ]
}
ABI
[{"inputs":[{"internalType":"address","name":"_ctf","type":"address"},{"internalType":"address","name":"_pyth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"DeadlineNotPassed","type":"error"},{"inputs":[],"name":"Flagged","type":"error"},{"inputs":[],"name":"InvalidDeadline","type":"error"},{"inputs":[],"name":"InvalidExpo","type":"error"},{"inputs":[],"name":"InvalidPayouts","type":"error"},{"inputs":[],"name":"InvalidPayoutsPrice","type":"error"},{"inputs":[],"name":"InvalidPrice","type":"error"},{"inputs":[],"name":"InvalidPriceFeed","type":"error"},{"inputs":[],"name":"NotFlagged","type":"error"},{"inputs":[],"name":"NotInitialized","type":"error"},{"inputs":[],"name":"NotPaused","type":"error"},{"inputs":[],"name":"NotReadyToResolve","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"PriceNotAvailable","type":"error"},{"inputs":[],"name":"PriceOutdated","type":"error"},{"inputs":[],"name":"Resolved","type":"error"},{"inputs":[],"name":"SafetyPeriodNotPassed","type":"error"},{"inputs":[],"name":"SafetyPeriodPassed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"questionId","type":"bytes32"},{"indexed":false,"internalType":"uint256[]","name":"payouts","type":"uint256[]"}],"name":"QuestionEmergencyResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"questionId","type":"bytes32"}],"name":"QuestionFlagged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"questionId","type":"bytes32"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"bytes32","name":"priceFeedId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"requestTimestamp","type":"uint256"},{"indexed":false,"internalType":"int64","name":"price","type":"int64"},{"indexed":false,"internalType":"int32","name":"expo","type":"int32"},{"indexed":false,"internalType":"uint256","name":"confTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"QuestionInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"questionId","type":"bytes32"}],"name":"QuestionReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"questionId","type":"bytes32"},{"indexed":true,"internalType":"int256","name":"settledPrice","type":"int256"},{"indexed":false,"internalType":"int32","name":"expo","type":"int32"},{"indexed":false,"internalType":"uint256[]","name":"payouts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"publishTime","type":"uint256"}],"name":"QuestionResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"questionId","type":"bytes32"}],"name":"QuestionUnflagged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_SAFETY_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ctf","outputs":[{"internalType":"contract IConditionalTokens","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"questionID","type":"bytes32"},{"internalType":"uint256[]","name":"payouts","type":"uint256[]"}],"name":"emergencyResolve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"questionID","type":"bytes32"}],"name":"flag","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"questionID","type":"bytes32"},{"internalType":"bytes32","name":"priceFeedID","type":"bytes32"},{"internalType":"int64","name":"price","type":"int64"},{"internalType":"int32","name":"expo","type":"int32"},{"internalType":"uint256","name":"confAge","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"questionID","type":"bytes32"},{"internalType":"bytes[]","name":"updateData","type":"bytes[]"}],"name":"parseFeedUniqueAndResolve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"pyth","outputs":[{"internalType":"contract IPyth","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"questions","outputs":[{"internalType":"uint256","name":"requestTimestamp","type":"uint256"},{"internalType":"int64","name":"price","type":"int64"},{"internalType":"int32","name":"expo","type":"int32"},{"internalType":"uint256","name":"confAge","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"emergencyResolutionTimestamp","type":"uint256"},{"internalType":"bytes32","name":"priceFeedId","type":"bytes32"},{"internalType":"bool","name":"initialized","type":"bool"},{"internalType":"bool","name":"resolved","type":"bool"},{"internalType":"address","name":"creator","type":"address"},{"internalType":"int16","name":"winningIndex","type":"int16"},{"internalType":"int64","name":"settledPrice","type":"int64"},{"internalType":"int32","name":"settledExpo","type":"int32"},{"internalType":"uint256","name":"publishTime","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"removeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"questionID","type":"bytes32"}],"name":"resolve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"questionID","type":"bytes32"}],"name":"unflag","outputs":[],"stateMutability":"nonpayable","type":"function"}]