Contract Diff Checker

Contract Name:
SportAMMRiskManager

Contract Source Code:

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

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

// internal
import "../utils/proxy/solidity-0.8.0/ProxyReentrancyGuard.sol";
import "../utils/proxy/solidity-0.8.0/ProxyOwned.sol";

// interface
import "../interfaces/ISportPositionalMarketManager.sol";

/// @title Sports AMM Risk contract
/// @author gruja
contract SportAMMRiskManager is Initializable, ProxyOwned, PausableUpgradeable, ProxyReentrancyGuard {
    /* ========== RISK MANAGER CONST VARIABLES ========== */
    uint public constant MIN_TAG_NUMBER = 9000;
    uint public constant MIN_CHILD_NUMBER = 10000;
    uint public constant MIN_PLAYER_PROPS_NUMBER = 11000;
    uint public constant DEFAULT_DYNAMIC_LIQUIDITY_CUTOFF_DIVIDER = 2e18;
    uint private constant ONE = 1e18;

    /* ========== RISK MANAGER STATE VARIABLES ========== */
    address public manager;
    uint public defaultCapPerGame;
    mapping(uint => uint) public capPerSport;
    mapping(uint => mapping(uint => uint)) public capPerSportAndChild;
    mapping(address => uint) public capPerMarket;

    uint public defaultRiskMultiplier;
    mapping(uint => uint) public riskMultiplierForSport;
    mapping(address => uint) public riskMultiplierPerMarket;

    uint public maxCap;
    uint public maxRiskMultiplier;

    mapping(uint => bool) public isMarketForSportOnePositional;
    mapping(uint => bool) public isMarketForPlayerPropsOnePositional;

    // @return specific min_spread per address
    mapping(uint => mapping(uint => uint)) public minSpreadPerSport;

    /// @return The maximum supported odd for sport
    mapping(uint => uint) public minSupportedOddsPerSport;

    /// @return The maximum supported odd for sport
    mapping(uint => uint) public maxSpreadPerSport;

    mapping(uint => uint) public dynamicLiquidityCutoffTimePerSport;
    mapping(uint => uint) public dynamicLiquidityCutoffDividerPerSport;

    address public sportsAMMCancellationPool;


    /* ========== CONSTRUCTOR ========== */

    function initialize(
        address _owner,
        address _manager,
        uint _defaultCapPerGame,
        uint[] memory _sportIds,
        uint[] memory _capsPerSport,
        uint[] memory _sportIdsForChilds,
        uint[] memory _childsIds,
        uint[] memory _capsForChilds,
        uint _defaultRiskMultiplier,
        uint[] memory _sportIdsForMultiplier,
        uint[] memory _riskMultiplierPerSport
    ) public initializer {
        setOwner(_owner);
        initNonReentrant();
        defaultCapPerGame = _defaultCapPerGame;
        defaultRiskMultiplier = _defaultRiskMultiplier;
        manager = _manager;

        for (uint i; i < _sportIds.length; i++) {
            capPerSport[_sportIds[i]] = _capsPerSport[i];
        }

        for (uint i; i < _sportIdsForChilds.length; i++) {
            capPerSportAndChild[_sportIdsForChilds[i]][_childsIds[i]] = _capsForChilds[i];
        }

        for (uint i; i < _sportIdsForMultiplier.length; i++) {
            riskMultiplierForSport[_sportIdsForMultiplier[i]] = _riskMultiplierPerSport[i];
        }
    }

    /* ========== VIEW FUNCTIONS ========== */

    /// @notice calculate which cap needs to be applied to the given market
    /// @param _market to get cap for
    /// @return toReturn cap to use
    function calculateCapToBeUsed(address _market) external view returns (uint toReturn) {
        (uint tag1, uint tag2) = ISportPositionalMarket(_market).getTags();
        return _calculateCapToBeUsed(_market, tag1, tag2);
    }

    /// @notice returns if market is in to much of a risk
    /// @param _totalSpent total spent on market
    /// @param _market for which is calculation done
    /// @return _isNotRisky true/false
    function isTotalSpendingLessThanTotalRisk(uint _totalSpent, address _market) external view returns (bool _isNotRisky) {
        (uint tag1, uint tag2) = ISportPositionalMarket(_market).getTags();
        uint capToBeUsed = _calculateCapToBeUsed(_market, tag1, tag2);
        uint riskMultiplier = _calculateRiskMultiplier(_market, tag1, tag2);
        return _totalSpent <= capToBeUsed * riskMultiplier;
    }

    /// @notice returns all data (caps, min spread, etc.) for given sports
    /// @param _sportIds sport ids
    /// @return _capsPerSport caps per sport
    /// @return _capsPerSportH caps per child H
    /// @return _capsPerSportT caps per child T
    /// @return _capsPerSportPP caps per child PP
    /// @return _minSpreadSport min spread per sport
    /// @return _minSpreadSportH min spread per child H
    /// @return _minSpreadSportT min spread per child T
    /// @return _minSpreadSportPP min spread per child PP
    function getAllDataForSports(uint[] memory _sportIds)
        external
        view
        returns (
            uint[] memory _capsPerSport,
            uint[] memory _capsPerSportH,
            uint[] memory _capsPerSportT,
            uint[] memory _capsPerSportPP,
            uint[] memory _minSpreadSport,
            uint[] memory _minSpreadSportH,
            uint[] memory _minSpreadSportT,
            uint[] memory _minSpreadSportPP
        )
    {
        _capsPerSport = new uint[](_sportIds.length);
        _capsPerSportH = new uint[](_sportIds.length);
        _capsPerSportT = new uint[](_sportIds.length);
        _capsPerSportPP = new uint[](_sportIds.length);
        _minSpreadSport = new uint[](_sportIds.length);
        _minSpreadSportH = new uint[](_sportIds.length);
        _minSpreadSportT = new uint[](_sportIds.length);
        _minSpreadSportPP = new uint[](_sportIds.length);

        for (uint i = 0; i < _sportIds.length; i++) {
            _capsPerSport[i] = capPerSport[_sportIds[i]];
            _capsPerSportH[i] = capPerSportAndChild[_sportIds[i]][MIN_CHILD_NUMBER + 1];
            _capsPerSportT[i] = capPerSportAndChild[_sportIds[i]][MIN_CHILD_NUMBER + 2];
            _capsPerSportPP[i] = capPerSportAndChild[_sportIds[i]][MIN_CHILD_NUMBER + 10];
            _minSpreadSport[i] = minSpreadPerSport[_sportIds[i]][0];
            _minSpreadSportH[i] = minSpreadPerSport[_sportIds[i]][MIN_CHILD_NUMBER + 1];
            _minSpreadSportT[i] = minSpreadPerSport[_sportIds[i]][MIN_CHILD_NUMBER + 2];
            _minSpreadSportPP[i] = minSpreadPerSport[_sportIds[i]][MIN_CHILD_NUMBER + 10];
        }
    }

    /* ========== INTERNALS ========== */

    function _calculateRiskMultiplier(
        address market,
        uint tag1,
        uint tag2
    ) internal view returns (uint toReturn) {
        uint marketRisk = riskMultiplierPerMarket[market];

        if (marketRisk == 0) {
            uint riskPerTag = riskMultiplierForSport[tag1];
            marketRisk = riskPerTag > 0 ? riskPerTag : defaultRiskMultiplier;
        }

        toReturn = marketRisk;
    }

    function _calculateCapToBeUsed(
        address market,
        uint tag1,
        uint tag2
    ) internal view returns (uint toReturn) {
        toReturn = capPerMarket[market];
        if (toReturn == 0) {
            uint capFirstTag = capPerSport[tag1];
            capFirstTag = capFirstTag > 0 ? capFirstTag : defaultCapPerGame;
            toReturn = capFirstTag;

            if (tag2 > 0) {
                uint capSecondTag = capPerSportAndChild[tag1][tag2];
                toReturn = capSecondTag > 0 ? capSecondTag : capFirstTag / 2;
            }
        }

        uint dynamicLiquidityCutoffTime = dynamicLiquidityCutoffTimePerSport[tag1];
        if (dynamicLiquidityCutoffTime > 0) {
            (uint maturity, ) = ISportPositionalMarket(market).times();
            uint timeToStart = maturity - block.timestamp;
            uint cutOffLiquidity = (toReturn * ONE) /
                (
                    dynamicLiquidityCutoffDividerPerSport[tag1] > 0
                        ? dynamicLiquidityCutoffDividerPerSport[tag1]
                        : DEFAULT_DYNAMIC_LIQUIDITY_CUTOFF_DIVIDER
                );
            if (timeToStart >= dynamicLiquidityCutoffTime) {
                toReturn = cutOffLiquidity;
            } else {
                uint remainingFromCutOff = toReturn - cutOffLiquidity;
                toReturn =
                    cutOffLiquidity +
                    (((dynamicLiquidityCutoffTime - timeToStart) * remainingFromCutOff) / dynamicLiquidityCutoffTime);
            }
        }
    }

    /* ========== CONTRACT MANAGEMENT ========== */

    /// @notice Setting the dynamic liquidity params
    /// @param _sportID The tagID used for sport (9004)
    /// @param _dynamicLiquidityCutoffTime when to start increasing the liquidity linearly, if 0 assume 100% liquidity all the time since market creation
    /// @param _dynamicLiquidityCutoffDivider e.g. if 2 it means liquidity up until cut off time is 50%, then increases linearly. if 0 use default
    function setDynamicLiquidityParamsPerSport(
        uint _sportID,
        uint _dynamicLiquidityCutoffTime,
        uint _dynamicLiquidityCutoffDivider
    ) external onlyOwner {
        require(_sportID > MIN_TAG_NUMBER, "Invalid tag for sport");
        dynamicLiquidityCutoffTimePerSport[_sportID] = _dynamicLiquidityCutoffTime;
        dynamicLiquidityCutoffDividerPerSport[_sportID] = _dynamicLiquidityCutoffDivider;
        emit SetDynamicLiquidityParams(_sportID, _dynamicLiquidityCutoffTime, _dynamicLiquidityCutoffDivider);
    }

    /// @notice Setting the Cap per spec. market
    /// @param _markets market addresses
    /// @param _capPerMarket The cap amount used for the specific markets
    function setCapPerMarket(address[] memory _markets, uint _capPerMarket) external {
        require(
            msg.sender == owner || ISportPositionalMarketManager(manager).isWhitelistedAddress(msg.sender),
            "Invalid sender"
        );
        require(_capPerMarket <= maxCap, "Invalid cap");
        for (uint i; i < _markets.length; i++) {
            require(_markets[i] != address(0), "Invalid address");
            capPerMarket[_markets[i]] = _capPerMarket;
            emit SetCapPerMarket(_markets[i], _capPerMarket);
        }
    }

    /// @notice Setting the Cap per Sport ID
    /// @param _sportID The tagID used for sport (9004)
    /// @param _childID The tagID used for childid (10002)
    /// @param _capPerChild The cap amount used for the sportID
    function setCapPerSportAndChild(
        uint _sportID,
        uint _childID,
        uint _capPerChild
    ) external onlyOwner {
        uint currentCapPerSport = capPerSport[_sportID] > 0 ? capPerSport[_sportID] : defaultCapPerGame;
        require(_capPerChild <= currentCapPerSport, "Invalid cap");
        require(_sportID > MIN_TAG_NUMBER, "Invalid tag for sport");
        require(_childID > MIN_CHILD_NUMBER, "Invalid tag for child");
        capPerSportAndChild[_sportID][_childID] = _capPerChild;
        emit SetCapPerSportAndChild(_sportID, _childID, _capPerChild);
    }

    /// @notice Setting the Cap per Sport ID
    /// @param _sportID The tagID used for each market
    /// @param _capPerSport The cap amount used for the sportID
    function setCapPerSport(uint _sportID, uint _capPerSport) external onlyOwner {
        require(_sportID > MIN_TAG_NUMBER, "Invalid tag for sport");
        require(_capPerSport <= maxCap, "Invalid cap");
        capPerSport[_sportID] = _capPerSport;
        emit SetCapPerSport(_sportID, _capPerSport);
    }

    /// @notice Setting the Cap per game default value
    /// @param _capPerGame default cap
    function setDefaultCapPerGame(uint _capPerGame) external onlyOwner {
        require(_capPerGame <= maxCap, "Invalid cap");
        defaultCapPerGame = _capPerGame;
        emit SetDefaultCapPerGame(_capPerGame);
    }

    /// @notice default risk multiplier
    /// @param _riskMultiplier risk multiplier
    function setDefaultRiskMultiplier(uint _riskMultiplier) external onlyOwner {
        require(_riskMultiplier <= maxRiskMultiplier, "Invalid multiplier");
        defaultRiskMultiplier = _riskMultiplier;
        emit SetDefaultRiskMultiplier(_riskMultiplier);
    }

    /// @notice Setting the risk multiplier per Sport ID
    /// @param _sportID The tagID used for each market
    /// @param _riskMultiplier The risk multiplier amount used for the sportID
    function setRiskMultiplierPerSport(uint _sportID, uint _riskMultiplier) external onlyOwner {
        require(_sportID > MIN_TAG_NUMBER, "Invalid tag for sport");
        require(_riskMultiplier <= maxRiskMultiplier, "Invalid multiplier");
        riskMultiplierForSport[_sportID] = _riskMultiplier;
        emit SetRiskMultiplierPerSport(_sportID, _riskMultiplier);
    }

    /// @notice Setting the risk multiplier per spec. market
    /// @param _markets market addresses
    /// @param _riskMultiplier The risk multiplier used for the specific markets
    function setRiskMultiplierMarket(address[] memory _markets, uint _riskMultiplier) external {
        require(
            msg.sender == owner || ISportPositionalMarketManager(manager).isWhitelistedAddress(msg.sender),
            "Invalid sender"
        );
        require(_riskMultiplier <= maxRiskMultiplier, "Invalid multiplier");
        for (uint i; i < _markets.length; i++) {
            require(_markets[i] != address(0), "Invalid address");
            riskMultiplierPerMarket[_markets[i]] = _riskMultiplier;
            emit SetRiskMultiplierPerMarket(_markets[i], _riskMultiplier);
        }
    }

    /// @notice Setting the Sport Positional Manager contract address
    /// @param _manager Address of Staking contract
    function setSportsPositionalMarketManager(address _manager) external onlyOwner {
        require(_manager != address(0), "Invalid address");
        manager = _manager;
        emit SetSportsPositionalMarketManager(_manager);
    }

    /// @notice Setting the max cap and risk per market
    /// @param _maxCap max cap per market
    /// @param _maxRisk max risk multiplier
    function setMaxCapAndRisk(uint _maxCap, uint _maxRisk) external onlyOwner {
        require(_maxCap > defaultCapPerGame && _maxRisk > defaultRiskMultiplier, "Invalid input");
        maxCap = _maxCap;
        maxRiskMultiplier = _maxRisk;
        emit SetMaxCapAndRisk(_maxCap, _maxRisk);
    }

    function setMinSupportedOddsAndMaxSpreadPerSportPerSport(
        uint _sportID,
        uint _minSupportedOdds,
        uint _maxSpreadPerSport
    ) external onlyOwner {
        minSupportedOddsPerSport[_sportID] = _minSupportedOdds;
        maxSpreadPerSport[_sportID] = _maxSpreadPerSport;
        emit SetMinSupportedOddsAndMaxSpreadPerSport(_sportID, _minSupportedOdds, _maxSpreadPerSport);
    }

    /// @notice Setting the Min Spread per Sport ID
    /// @param _tag1 The first tagID used for each market
    /// @param _tag2 The second tagID used for each market
    /// @param _minSpread The min spread amount used for the sportID
    function setMinSpreadPerSport(
        uint _tag1,
        uint _tag2,
        uint _minSpread
    ) external onlyOwner {
        minSpreadPerSport[_tag1][_tag2] = _minSpread;
        emit SetMinSpreadPerSport(_tag1, _tag2, _minSpread);
    }

    /// @notice setting one positional sport
    /// @param _sportID tag id for sport
    /// @param _flag is one positional sport flag
    function setSportOnePositional(uint _sportID, bool _flag) external onlyOwner {
        require(_sportID > MIN_TAG_NUMBER, "Invalid tag for sport");
        require(isMarketForSportOnePositional[_sportID] != _flag, "Invalid flag");
        isMarketForSportOnePositional[_sportID] = _flag;
        emit SetSportOnePositional(_sportID, _flag);
    }

    /// @notice setting one positional sport
    /// @param _playerPropsOptionTag tag id for PP
    /// @param _flag is one positional sport flag
    function setPlayerPropsOnePositional(uint _playerPropsOptionTag, bool _flag) external onlyOwner {
        require(_playerPropsOptionTag > MIN_PLAYER_PROPS_NUMBER, "Invalid tag for player props");
        require(isMarketForPlayerPropsOnePositional[_playerPropsOptionTag] != _flag, "Invalid flag");
        isMarketForPlayerPropsOnePositional[_playerPropsOptionTag] = _flag;
        emit SetPlayerPropsOnePositional(_playerPropsOptionTag, _flag);
    }

    function getMinSpreadToUse(
        bool useDefaultMinSpread,
        address market,
        uint min_spread,
        uint min_spreadPerAddress
    ) external view returns (uint min_spreadToUse) {
        (uint tag1, uint tag2) = ISportPositionalMarket(market).getTags();
        uint minSpreadByTags = minSpreadPerSport[tag1][tag2];
        uint minSpreadByPrimaryTag = minSpreadPerSport[tag1][0];
        uint spreadForTag = tag2 > 0 && minSpreadByTags > 0 ? minSpreadByTags : minSpreadByPrimaryTag;
        min_spreadToUse = useDefaultMinSpread
            ? (spreadForTag > 0 ? spreadForTag : min_spread)
            : (min_spreadPerAddress > 0 ? min_spreadPerAddress : (spreadForTag > 0 ? spreadForTag : min_spread));
    }

    function getMaxSpreadForMarket(address _market, uint max_spread) external view returns (uint maxSpread) {
        (uint tag1, ) = ISportPositionalMarket(_market).getTags();
        uint _maxSpreadPerSport = maxSpreadPerSport[tag1];
        maxSpread = _maxSpreadPerSport > 0 ? _maxSpreadPerSport : max_spread;
    }

    function getCapAndMaxSpreadForMarket(address _market, uint max_spread) public view returns (uint cap, uint maxSpread) {
        (uint tag1, uint tag2) = ISportPositionalMarket(_market).getTags();
        uint _maxSpreadPerSport = maxSpreadPerSport[tag1];
        maxSpread = _maxSpreadPerSport > 0 ? _maxSpreadPerSport : max_spread;
        cap = _calculateCapToBeUsed(_market, tag1, tag2);
    }

    function getCapMaxSpreadAndMinOddsForMarket(
        address _market,
        uint max_spread,
        uint minSupportedOdds
    )
        external
        view
        returns (
            uint cap,
            uint maxSpread,
            uint minOddsForMarket
        )
    {
        (uint tag1, uint tag2) = ISportPositionalMarket(_market).getTags();
        uint _maxSpreadPerSport = maxSpreadPerSport[tag1];
        maxSpread = _maxSpreadPerSport > 0 ? _maxSpreadPerSport : max_spread;
        cap = _calculateCapToBeUsed(_market, tag1, tag2);
        uint _minSupportedOddsPerSport = minSupportedOddsPerSport[tag1];
        minOddsForMarket = _minSupportedOddsPerSport > 0 ? _minSupportedOddsPerSport : minSupportedOdds;
    }

    function getMinOddsForMarket(address _market, uint minSupportedOdds) external view returns (uint minOdds) {
        (uint tag1, ) = ISportPositionalMarket(_market).getTags();
        uint _minSupportedOddsPerSport = minSupportedOddsPerSport[tag1];
        minOdds = _minSupportedOddsPerSport > 0 ? _minSupportedOddsPerSport : minSupportedOdds;
    }

    /* ========== MODIFIERS ========== */
    /* ========== EVENTS ========== */
    event SetCapPerSport(uint _sport, uint _cap);
    event SetCapPerMarket(address _market, uint _cap);
    event SetCapPerSportAndChild(uint _sport, uint _child, uint _cap);
    event SetSportsPositionalMarketManager(address _manager);
    event SetDefaultCapPerGame(uint _cap);
    event SetDefaultRiskMultiplier(uint _riskMultiplier);
    event SetRiskMultiplierPerSport(uint _sport, uint _riskMultiplier);
    event SetRiskMultiplierPerMarket(address _market, uint _riskMultiplier);
    event SetMaxCapAndRisk(uint _maxCap, uint _maxRisk);
    event SetMinSpreadPerSport(uint _tag1, uint _tag2, uint _spread);
    event SetMinSupportedOddsAndMaxSpreadPerSport(uint _sport, uint _minSupportedOddsPerSport, uint _maxSpreadPerSport);
    event SetSportOnePositional(uint _sport, bool _flag);
    event SetPlayerPropsOnePositional(uint _playerPropsOptionTag, bool _flag);
    event SetDynamicLiquidityParams(uint _sport, uint _dynamicLiquidityCutoffTime, uint _dynamicLiquidityCutoffDivider);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Context_init_unchained();
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        require(!paused(), "Pausable: paused");
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        require(paused(), "Pausable: not paused");
        _;
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier
 * available, which can be aplied 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.
 */
contract ProxyReentrancyGuard {
    /// @dev counter to allow mutex lock with only one SSTORE operation
    uint256 private _guardCounter;
    bool private _initialized;

    function initNonReentrant() public {
        require(!_initialized, "Already initialized");
        _initialized = true;
        _guardCounter = 1;
    }

    /**
     * @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 make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _guardCounter += 1;
        uint256 localCounter = _guardCounter;
        _;
        require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// Clone of syntetix contract without constructor
contract ProxyOwned {
    address public owner;
    address public nominatedOwner;
    bool private _initialized;
    bool private _transferredAtInit;

    function setOwner(address _owner) public {
        require(_owner != address(0), "Owner address cannot be 0");
        require(!_initialized, "Already initialized, use nominateNewOwner");
        _initialized = true;
        owner = _owner;
        emit OwnerChanged(address(0), _owner);
    }

    function nominateNewOwner(address _owner) external onlyOwner {
        nominatedOwner = _owner;
        emit OwnerNominated(_owner);
    }

    function acceptOwnership() external {
        require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
        emit OwnerChanged(owner, nominatedOwner);
        owner = nominatedOwner;
        nominatedOwner = address(0);
    }

    function transferOwnershipAtInit(address proxyAddress) external onlyOwner {
        require(proxyAddress != address(0), "Invalid address");
        require(!_transferredAtInit, "Already transferred");
        owner = proxyAddress;
        _transferredAtInit = true;
        emit OwnerChanged(owner, proxyAddress);
    }

    modifier onlyOwner {
        _onlyOwner();
        _;
    }

    function _onlyOwner() private view {
        require(msg.sender == owner, "Only the contract owner may perform this action");
    }

    event OwnerNominated(address newOwner);
    event OwnerChanged(address oldOwner, address newOwner);
}

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

import "../interfaces/ISportPositionalMarket.sol";

interface ISportPositionalMarketManager {
    /* ========== VIEWS / VARIABLES ========== */

    function marketCreationEnabled() external view returns (bool);

    function totalDeposited() external view returns (uint);

    function numActiveMarkets() external view returns (uint);

    function activeMarkets(uint index, uint pageSize) external view returns (address[] memory);

    function numMaturedMarkets() external view returns (uint);

    function maturedMarkets(uint index, uint pageSize) external view returns (address[] memory);

    function isActiveMarket(address candidate) external view returns (bool);

    function isDoubleChanceMarket(address candidate) external view returns (bool);

    function doesSportSupportDoubleChance(uint _sport) external view returns (bool);

    function isDoubleChanceSupported() external view returns (bool);

    function isKnownMarket(address candidate) external view returns (bool);

    function getActiveMarketAddress(uint _index) external view returns (address);

    function transformCollateral(uint value) external view returns (uint);

    function reverseTransformCollateral(uint value) external view returns (uint);

    function isMarketPaused(address _market) external view returns (bool);

    function expiryDuration() external view returns (uint);

    function isWhitelistedAddress(address _address) external view returns (bool);

    function getOddsObtainer() external view returns (address obtainer);

    /* ========== MUTATIVE FUNCTIONS ========== */

    function createMarket(
        bytes32 gameId,
        string memory gameLabel,
        uint maturity,
        uint initialMint, // initial sUSD to mint options for,
        uint positionCount,
        uint[] memory tags,
        bool isChild,
        address parentMarket
    ) external returns (ISportPositionalMarket);

    function setMarketPaused(address _market, bool _paused) external;

    function updateDatesForMarket(address _market, uint256 _newStartTime) external;

    function resolveMarket(address market, uint outcome) external;

    function expireMarkets(address[] calldata market) external;

    function transferSusdTo(
        address sender,
        address receiver,
        uint amount
    ) external;

    function queryMintsAndMaturityStatusForPlayerProps(address[] memory _playerPropsMarkets)
        external
        view
        returns (
            bool[] memory _hasAnyMintsArray,
            bool[] memory _isMaturedArray,
            bool[] memory _isResolvedArray,
            uint[] memory _maturities
        );
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @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 ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
        __Context_init_unchained();
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
    uint256[50] private __gap;
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;

import "../interfaces/IPositionalMarketManager.sol";
import "../interfaces/IPosition.sol";
import "../interfaces/IPriceFeed.sol";

interface ISportPositionalMarket {
    /* ========== TYPES ========== */

    enum Phase {
        Trading,
        Maturity,
        Expiry
    }
    enum Side {
        Cancelled,
        Home,
        Away,
        Draw
    }

    /* ========== VIEWS / VARIABLES ========== */

    function getOptions()
        external
        view
        returns (
            IPosition home,
            IPosition away,
            IPosition draw
        );

    function times() external view returns (uint maturity, uint destruction);

    function getGameDetails() external view returns (bytes32 gameId, string memory gameLabel);

    function getGameId() external view returns (bytes32);

    function deposited() external view returns (uint);

    function optionsCount() external view returns (uint);

    function creator() external view returns (address);

    function resolved() external view returns (bool);

    function cancelled() external view returns (bool);

    function paused() external view returns (bool);

    function phase() external view returns (Phase);

    function canResolve() external view returns (bool);

    function result() external view returns (Side);

    function isChild() external view returns (bool);

    function optionsInitialized() external view returns (bool);

    function tags(uint idx) external view returns (uint);

    function getTags() external view returns (uint tag1, uint tag2);

    function getTagsLength() external view returns (uint tagsLength);

    function getParentMarketPositions() external view returns (IPosition position1, IPosition position2);

    function getParentMarketPositionsUint() external view returns (uint position1, uint position2);

    function getStampedOdds()
        external
        view
        returns (
            uint,
            uint,
            uint
        );

    function balancesOf(address account)
        external
        view
        returns (
            uint home,
            uint away,
            uint draw
        );

    function totalSupplies()
        external
        view
        returns (
            uint home,
            uint away,
            uint draw
        );

    function isDoubleChance() external view returns (bool);

    function parentMarket() external view returns (ISportPositionalMarket);

    /* ========== MUTATIVE FUNCTIONS ========== */

    function setPaused(bool _paused) external;

    function updateDates(uint256 _maturity, uint256 _expiry) external;

    function mint(uint value) external;

    function exerciseOptions() external;

    function restoreInvalidOdds(
        uint _homeOdds,
        uint _awayOdds,
        uint _drawOdds
    ) external;
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.5.16;

import "../interfaces/IPositionalMarket.sol";

interface IPositionalMarketManager {
    /* ========== VIEWS / VARIABLES ========== */

    function durations() external view returns (uint expiryDuration, uint maxTimeToMaturity);

    function capitalRequirement() external view returns (uint);

    function marketCreationEnabled() external view returns (bool);

    function onlyAMMMintingAndBurning() external view returns (bool);

    function transformCollateral(uint value) external view returns (uint);

    function reverseTransformCollateral(uint value) external view returns (uint);

    function totalDeposited() external view returns (uint);

    function numActiveMarkets() external view returns (uint);

    function activeMarkets(uint index, uint pageSize) external view returns (address[] memory);

    function numMaturedMarkets() external view returns (uint);

    function maturedMarkets(uint index, uint pageSize) external view returns (address[] memory);

    function isActiveMarket(address candidate) external view returns (bool);

    function isKnownMarket(address candidate) external view returns (bool);

    function getThalesAMM() external view returns (address);

    /* ========== MUTATIVE FUNCTIONS ========== */

    function createMarket(
        bytes32 oracleKey,
        uint strikePrice,
        uint maturity,
        uint initialMint // initial sUSD to mint options for,
    ) external returns (IPositionalMarket);

    function resolveMarket(address market) external;

    function expireMarkets(address[] calldata market) external;

    function transferSusdTo(
        address sender,
        address receiver,
        uint amount
    ) external;
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.5.16;

import "./IPositionalMarket.sol";

interface IPosition {
    /* ========== VIEWS / VARIABLES ========== */

    function getBalanceOf(address account) external view returns (uint);

    function getTotalSupply() external view returns (uint);

    function exerciseWithAmount(address claimant, uint amount) external;
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.5.16;

interface IPriceFeed {
    // Structs
    struct RateAndUpdatedTime {
        uint216 rate;
        uint40 time;
    }

    // Mutative functions
    function addAggregator(bytes32 currencyKey, address aggregatorAddress) external;

    function removeAggregator(bytes32 currencyKey) external;

    // Views

    function rateForCurrency(bytes32 currencyKey) external view returns (uint);

    function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time);

    function getRates() external view returns (uint[] memory);

    function getCurrencies() external view returns (bytes32[] memory);
}

// SPDX-License-Identifier: MIT

pragma solidity >=0.5.16;

import "../interfaces/IPositionalMarketManager.sol";
import "../interfaces/IPosition.sol";
import "../interfaces/IPriceFeed.sol";

interface IPositionalMarket {
    /* ========== TYPES ========== */

    enum Phase {
        Trading,
        Maturity,
        Expiry
    }
    enum Side {
        Up,
        Down
    }

    /* ========== VIEWS / VARIABLES ========== */

    function getOptions() external view returns (IPosition up, IPosition down);

    function times() external view returns (uint maturity, uint destructino);

    function getOracleDetails()
        external
        view
        returns (
            bytes32 key,
            uint strikePrice,
            uint finalPrice
        );

    function fees() external view returns (uint poolFee, uint creatorFee);

    function deposited() external view returns (uint);

    function creator() external view returns (address);

    function resolved() external view returns (bool);

    function phase() external view returns (Phase);

    function oraclePrice() external view returns (uint);

    function oraclePriceAndTimestamp() external view returns (uint price, uint updatedAt);

    function canResolve() external view returns (bool);

    function result() external view returns (Side);

    function balancesOf(address account) external view returns (uint up, uint down);

    function totalSupplies() external view returns (uint up, uint down);

    function getMaximumBurnable(address account) external view returns (uint amount);

    /* ========== MUTATIVE FUNCTIONS ========== */

    function mint(uint value) external;

    function exerciseOptions() external returns (uint);

    function burnOptions(uint amount) external;

    function burnOptionsMaximum() external;
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):