ETH Price: $3,116.70 (-3.91%)
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Request Live Tra...394197172025-12-13 12:26:2137 mins ago1765628781IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.00000020.00050103
Request Live Tra...394196492025-12-13 12:24:0539 mins ago1765628645IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000160.00040848
Request Live Tra...394172232025-12-13 11:03:132 hrs ago1765623793IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000490.00120245
Request Live Tra...394150772025-12-13 9:51:413 hrs ago1765619501IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000590.00137644
Request Live Tra...394150592025-12-13 9:51:053 hrs ago1765619465IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000590.001376
Request Live Tra...394150102025-12-13 9:49:273 hrs ago1765619367IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000490.00120102
Request Live Tra...394149942025-12-13 9:48:553 hrs ago1765619335IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000490.00120144
Request Live Tra...394141832025-12-13 9:21:533 hrs ago1765617713IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000520.00126152
Request Live Tra...394131622025-12-13 8:47:514 hrs ago1765615671IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000870.00211077
Request Live Tra...394111122025-12-13 7:39:315 hrs ago1765611571IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000490.00120304
Request Live Tra...394053752025-12-13 4:28:178 hrs ago1765600097IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000840.00205348
Request Live Tra...394048262025-12-13 4:09:598 hrs ago1765598999IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002140.0052
Request Live Tra...394047842025-12-13 4:08:358 hrs ago1765598915IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002140.0052
Request Live Tra...394042072025-12-13 3:49:219 hrs ago1765597761IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000640.00157266
Request Live Tra...394040822025-12-13 3:45:119 hrs ago1765597511IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002130.0052
Request Live Tra...394035792025-12-13 3:28:259 hrs ago1765596505IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002120.0052
Request Live Tra...394035402025-12-13 3:27:079 hrs ago1765596427IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002120.0052
Request Live Tra...394035142025-12-13 3:26:159 hrs ago1765596375IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002120.0052
Request Live Tra...394034482025-12-13 3:24:039 hrs ago1765596243IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002140.0052
Request Live Tra...394034432025-12-13 3:23:539 hrs ago1765596233IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002140.0052
Request Live Tra...394034082025-12-13 3:22:439 hrs ago1765596163IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002140.0052
Request Live Tra...394034042025-12-13 3:22:359 hrs ago1765596155IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002140.00520029
Request Live Tra...394033812025-12-13 3:21:499 hrs ago1765596109IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002140.00520006
Request Live Tra...394033782025-12-13 3:21:439 hrs ago1765596103IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000002140.00520043
Request Live Tra...394033222025-12-13 3:19:519 hrs ago1765595991IN
0xC7BD9193...2a8Bc3ef1
0 ETH0.000000490.0012
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LiveTradingProcessor

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 100 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// external
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

import "../../interfaces/ISportsAMMV2.sol";
import "../../interfaces/IFreeBetsHolder.sol";
import "../../interfaces/ILiveTradingProcessor.sol";
import "../../interfaces/IStakingThalesBettingProxy.sol";

contract LiveTradingProcessor is ChainlinkClient, Ownable, Pausable {
    using Chainlink for Chainlink.Request;
    using SafeERC20 for IERC20;

    uint private constant ONE = 1e18;

    ISportsAMMV2 public sportsAMM;

    address freeBetsHolder;

    bytes32 public jobSpecId;

    uint public paymentAmount;

    uint public maxAllowedExecutionDelay = 60;

    mapping(bytes32 => ILiveTradingProcessor.LiveTradeData) public requestIdToTradeData;
    mapping(bytes32 => address) public requestIdToRequester;
    mapping(bytes32 => bool) public requestIdToFulfillAllowed;
    mapping(bytes32 => bool) public requestIdFulfilled;
    mapping(bytes32 => uint) public timestampPerRequest;

    uint public requestCounter;
    mapping(uint => bytes32) public counterToRequestId;

    address public stakingThalesBettingProxy;

    mapping(bytes32 => address) public requestIdToTicketId;

    constructor(
        address _link,
        address _oracle,
        address _sportsAMM,
        bytes32 _jobSpecId,
        uint _paymentAmount
    ) Ownable(msg.sender) {
        setChainlinkToken(_link);
        setChainlinkOracle(_oracle);
        sportsAMM = ISportsAMMV2(_sportsAMM);
        jobSpecId = _jobSpecId;
        paymentAmount = _paymentAmount;
    }

    /// @notice requestLiveTrade
    /// @param _liveTradeData for which to request a live trade
    function requestLiveTrade(
        ILiveTradingProcessor.LiveTradeData calldata _liveTradeData
    ) external whenNotPaused returns (bytes32 requestId) {
        require(
            sportsAMM.riskManager().liveTradingPerSportAndTypeEnabled(_liveTradeData._sportId, _liveTradeData._typeId),
            "Live trading not enabled on _sportId"
        );

        Chainlink.Request memory req;

        req = buildChainlinkRequest(jobSpecId, address(this), this.fulfillLiveTrade.selector);

        req.add("gameId", _liveTradeData._gameId);
        req.addUint("sportId", _liveTradeData._sportId);
        req.addUint("typeId", _liveTradeData._typeId);
        req.addInt("line", _liveTradeData._line);
        req.addUint("position", _liveTradeData._position);
        req.addUint("buyInAmount", _liveTradeData._buyInAmount);
        req.addUint("expectedQuote", _liveTradeData._expectedQuote);
        req.addUint("additionalSlippage", _liveTradeData._additionalSlippage);
        req.addUint("playerId", _liveTradeData._playerId); // 🆕
        // 👇 Add msg.sender as a hex string
        req.add("requester", Strings.toHexString(msg.sender));

        requestId = sendChainlinkRequest(req, paymentAmount);
        timestampPerRequest[requestId] = block.timestamp;
        requestIdToTradeData[requestId] = _liveTradeData;
        requestIdToRequester[requestId] = msg.sender;

        counterToRequestId[requestCounter] = requestId;

        emit LiveTradeRequested(
            msg.sender,
            requestCounter,
            requestId,
            stringToBytes32(_liveTradeData._gameId),
            _liveTradeData._sportId,
            _liveTradeData._typeId,
            _liveTradeData._line,
            _liveTradeData._position,
            _liveTradeData._buyInAmount,
            _liveTradeData._expectedQuote,
            _liveTradeData._collateral
        );
        requestCounter++;
    }

    /// @notice fulfillLiveTrade
    /// @param _requestId which is being fulfilled
    /// @param _allow whether the live trade should go through
    /// @param _approvedQuote what will be the actual payout
    function fulfillLiveTrade(
        bytes32 _requestId,
        bool _allow,
        uint _approvedQuote
    ) external whenNotPaused recordChainlinkFulfillment(_requestId) {
        //might be redundant as already done by Chainlink Client, but making double sure
        require(!requestIdFulfilled[_requestId], "Request ID already fulfilled");
        require((timestampPerRequest[_requestId] + maxAllowedExecutionDelay) > block.timestamp, "Request timed out");

        ILiveTradingProcessor.LiveTradeData memory lTradeData = requestIdToTradeData[_requestId];
        address requester = requestIdToRequester[_requestId];

        require(
            ((ONE * _approvedQuote) / lTradeData._expectedQuote) <= (ONE + lTradeData._additionalSlippage),
            "Slippage too high"
        );

        if (_allow) {
            ISportsAMMV2.TradeData[] memory tradeData = new ISportsAMMV2.TradeData[](1);
            bytes32[] memory merkleProofs;
            uint[] memory odds = new uint[](225);
            odds[lTradeData._position] = _approvedQuote;
            ISportsAMMV2.CombinedPosition[][] memory comPositions = new ISportsAMMV2.CombinedPosition[][](225);

            tradeData[0] = ISportsAMMV2.TradeData(
                stringToBytes32(lTradeData._gameId),
                lTradeData._sportId,
                lTradeData._typeId, //type
                block.timestamp + 60, //maturity, hardcode to timestamp with buffer.
                0, //status
                lTradeData._line, //line
                lTradeData._playerId, //playerId
                odds, //odds[]
                merkleProofs, //merkleProof[]
                lTradeData._position,
                comPositions //combinedPositions[]
            );

            if (requester == stakingThalesBettingProxy) {
                IStakingThalesBettingProxy(stakingThalesBettingProxy).preConfirmLiveTrade(
                    _requestId,
                    lTradeData._buyInAmount
                );
            }

            address _createdTicket = sportsAMM.tradeLive(
                tradeData,
                lTradeData._buyInAmount,
                _approvedQuote,
                requester,
                lTradeData._referrer,
                lTradeData._collateral
            );
            requestIdToTicketId[_requestId] = _createdTicket;

            if (requester == freeBetsHolder) {
                IFreeBetsHolder(freeBetsHolder).confirmLiveTrade(
                    _requestId,
                    _createdTicket,
                    lTradeData._buyInAmount,
                    lTradeData._collateral
                );
            } else if (requester == stakingThalesBettingProxy) {
                IStakingThalesBettingProxy(stakingThalesBettingProxy).confirmLiveTrade(
                    _requestId,
                    _createdTicket,
                    lTradeData._buyInAmount
                );
            }
        }
        requestIdToFulfillAllowed[_requestId] = _allow;
        requestIdFulfilled[_requestId] = true;

        emit LiveTradeFulfilled(
            requester,
            _requestId,
            _allow,
            stringToBytes32(lTradeData._gameId),
            lTradeData._sportId,
            lTradeData._typeId,
            lTradeData._line,
            lTradeData._position,
            lTradeData._buyInAmount,
            _approvedQuote,
            lTradeData._collateral,
            block.timestamp
        );
    }

    /// @notice withdraw collateral in the contract
    /// @param collateral the collateral address
    /// @param recipient the recipient of the collateral
    function withdrawCollateral(address collateral, address recipient) external onlyOwner {
        IERC20(collateral).safeTransfer(recipient, IERC20(collateral).balanceOf(address(this)));
    }

    //////////// SETTERS

    /// @notice pause live trading
    /// @param _setPausing whether to pause or unpause
    function setPaused(bool _setPausing) external onlyOwner {
        _setPausing ? _pause() : _unpause();
    }

    /// @notice setConfiguration
    /// @param _link paymentAmount token
    /// @param _oracle CL node that will execute the requests
    /// @param _sportsAMM address
    /// @param _jobSpecId CL node job spec ID
    /// @param _paymentAmount amount of paymentAmount token for each request
    function setConfiguration(
        address _link,
        address _oracle,
        address _sportsAMM,
        bytes32 _jobSpecId,
        uint _paymentAmount
    ) external onlyOwner {
        setChainlinkToken(_link);
        setChainlinkOracle(_oracle);
        sportsAMM = ISportsAMMV2(_sportsAMM);
        jobSpecId = _jobSpecId;
        paymentAmount = _paymentAmount;
        emit ContextReset(_link, _oracle, _sportsAMM, _jobSpecId, _paymentAmount);
    }

    /// @notice sets the FreeBetsHolder address, required for handling ticket claiming via FreeBetsHolder
    function setFreeBetsHolder(address _freeBetsHolder) external onlyOwner {
        freeBetsHolder = _freeBetsHolder;
        emit SetFreeBetsHolder(_freeBetsHolder);
    }

    /// @notice sets the stakingThalesBettingProxy address, required for handling ticket claiming via StakingThalesBettingProxy
    function setStakingThalesBettingProxy(address _stakingThalesBettingProxy) external onlyOwner {
        stakingThalesBettingProxy = _stakingThalesBettingProxy;
        emit SetStakingThalesBettingProxy(_stakingThalesBettingProxy);
    }

    /// @notice setMaxAllowedExecutionDelay
    /// @param _maxAllowedExecutionDelay maximum allowed buffer for the CL request to be executed, defaulted at 60 seconds
    function setMaxAllowedExecutionDelay(uint _maxAllowedExecutionDelay) external onlyOwner {
        maxAllowedExecutionDelay = _maxAllowedExecutionDelay;
        emit SetMaxAllowedExecutionDelay(_maxAllowedExecutionDelay);
    }

    //// GETTERS

    /// @notice gets trade data struct for specified request ID
    /// @param requestId request ID
    /// @return liveTradeData
    function getTradeData(bytes32 requestId) external view returns (ILiveTradingProcessor.LiveTradeData memory) {
        return requestIdToTradeData[requestId];
    }

    //// UTILITY
    function stringToBytes32(string memory source) internal pure returns (bytes32 result) {
        bytes memory tempEmptyStringTest = bytes(source);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }
        assembly {
            result := mload(add(source, 32))
        }
    }

    /////// EVENTS
    event ContextReset(address _link, address _oracle, address _sportsAMM, bytes32 _jobSpecId, uint _paymentAmount);
    event LiveTradeRequested(
        address requester,
        uint requestCounter,
        bytes32 requestId,
        bytes32 _gameId,
        uint16 _sportId,
        uint16 _typeId,
        int24 _line,
        uint8 _position,
        uint _buyInAmount,
        uint _expectedQuote,
        address _collateral
    );
    event LiveTradeFulfilled(
        address requester,
        bytes32 requestId,
        bool _allow,
        bytes32 _gameId,
        uint16 _sportId,
        uint16 _typeId,
        int24 _line,
        uint8 _position,
        uint _buyInAmount,
        uint _expectedQuote,
        address _collateral,
        uint timestamp
    );
    event SetMaxAllowedExecutionDelay(uint _maxAllowedExecutionDelay);
    event SetFreeBetsHolder(address _freeBetsHolder);
    event SetStakingThalesBettingProxy(address _stakingThalesBettingProxy);
}

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

import {CBORChainlink} from "./vendor/CBORChainlink.sol";
import {BufferChainlink} from "./vendor/BufferChainlink.sol";

/**
 * @title Library for common Chainlink functions
 * @dev Uses imported CBOR library for encoding to buffer
 */
library Chainlink {
  uint256 internal constant defaultBufferSize = 256; // solhint-disable-line const-name-snakecase

  using CBORChainlink for BufferChainlink.buffer;

  struct Request {
    bytes32 id;
    address callbackAddress;
    bytes4 callbackFunctionId;
    uint256 nonce;
    BufferChainlink.buffer buf;
  }

  /**
   * @notice Initializes a Chainlink request
   * @dev Sets the ID, callback address, and callback function signature on the request
   * @param self The uninitialized request
   * @param jobId The Job Specification ID
   * @param callbackAddr The callback address
   * @param callbackFunc The callback function signature
   * @return The initialized request
   */
  function initialize(
    Request memory self,
    bytes32 jobId,
    address callbackAddr,
    bytes4 callbackFunc
  ) internal pure returns (Chainlink.Request memory) {
    BufferChainlink.init(self.buf, defaultBufferSize);
    self.id = jobId;
    self.callbackAddress = callbackAddr;
    self.callbackFunctionId = callbackFunc;
    return self;
  }

  /**
   * @notice Sets the data for the buffer without encoding CBOR on-chain
   * @dev CBOR can be closed with curly-brackets {} or they can be left off
   * @param self The initialized request
   * @param data The CBOR data
   */
  function setBuffer(Request memory self, bytes memory data) internal pure {
    BufferChainlink.init(self.buf, data.length);
    BufferChainlink.append(self.buf, data);
  }

  /**
   * @notice Adds a string value to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param value The string value to add
   */
  function add(
    Request memory self,
    string memory key,
    string memory value
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.encodeString(value);
  }

  /**
   * @notice Adds a bytes value to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param value The bytes value to add
   */
  function addBytes(
    Request memory self,
    string memory key,
    bytes memory value
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.encodeBytes(value);
  }

  /**
   * @notice Adds a int256 value to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param value The int256 value to add
   */
  function addInt(
    Request memory self,
    string memory key,
    int256 value
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.encodeInt(value);
  }

  /**
   * @notice Adds a uint256 value to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param value The uint256 value to add
   */
  function addUint(
    Request memory self,
    string memory key,
    uint256 value
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.encodeUInt(value);
  }

  /**
   * @notice Adds an array of strings to the request with a given key name
   * @param self The initialized request
   * @param key The name of the key
   * @param values The array of string values to add
   */
  function addStringArray(
    Request memory self,
    string memory key,
    string[] memory values
  ) internal pure {
    self.buf.encodeString(key);
    self.buf.startArray();
    for (uint256 i = 0; i < values.length; i++) {
      self.buf.encodeString(values[i]);
    }
    self.buf.endSequence();
  }
}

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

import "./Chainlink.sol";
import "./interfaces/ENSInterface.sol";
import "./interfaces/LinkTokenInterface.sol";
import "./interfaces/ChainlinkRequestInterface.sol";
import "./interfaces/OperatorInterface.sol";
import "./interfaces/PointerInterface.sol";
import {ENSResolver as ENSResolver_Chainlink} from "./vendor/ENSResolver.sol";

/**
 * @title The ChainlinkClient contract
 * @notice Contract writers can inherit this contract in order to create requests for the
 * Chainlink network
 */
abstract contract ChainlinkClient {
  using Chainlink for Chainlink.Request;

  uint256 internal constant LINK_DIVISIBILITY = 10**18;
  uint256 private constant AMOUNT_OVERRIDE = 0;
  address private constant SENDER_OVERRIDE = address(0);
  uint256 private constant ORACLE_ARGS_VERSION = 1;
  uint256 private constant OPERATOR_ARGS_VERSION = 2;
  bytes32 private constant ENS_TOKEN_SUBNAME = keccak256("link");
  bytes32 private constant ENS_ORACLE_SUBNAME = keccak256("oracle");
  address private constant LINK_TOKEN_POINTER = 0xC89bD4E1632D3A43CB03AAAd5262cbe4038Bc571;

  ENSInterface private s_ens;
  bytes32 private s_ensNode;
  LinkTokenInterface private s_link;
  OperatorInterface private s_oracle;
  uint256 private s_requestCount = 1;
  mapping(bytes32 => address) private s_pendingRequests;

  event ChainlinkRequested(bytes32 indexed id);
  event ChainlinkFulfilled(bytes32 indexed id);
  event ChainlinkCancelled(bytes32 indexed id);

  /**
   * @notice Creates a request that can hold additional parameters
   * @param specId The Job Specification ID that the request will be created for
   * @param callbackAddr address to operate the callback on
   * @param callbackFunctionSignature function signature to use for the callback
   * @return A Chainlink Request struct in memory
   */
  function buildChainlinkRequest(
    bytes32 specId,
    address callbackAddr,
    bytes4 callbackFunctionSignature
  ) internal pure returns (Chainlink.Request memory) {
    Chainlink.Request memory req;
    return req.initialize(specId, callbackAddr, callbackFunctionSignature);
  }

  /**
   * @notice Creates a request that can hold additional parameters
   * @param specId The Job Specification ID that the request will be created for
   * @param callbackFunctionSignature function signature to use for the callback
   * @return A Chainlink Request struct in memory
   */
  function buildOperatorRequest(bytes32 specId, bytes4 callbackFunctionSignature)
    internal
    view
    returns (Chainlink.Request memory)
  {
    Chainlink.Request memory req;
    return req.initialize(specId, address(this), callbackFunctionSignature);
  }

  /**
   * @notice Creates a Chainlink request to the stored oracle address
   * @dev Calls `chainlinkRequestTo` with the stored oracle address
   * @param req The initialized Chainlink Request
   * @param payment The amount of LINK to send for the request
   * @return requestId The request ID
   */
  function sendChainlinkRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) {
    return sendChainlinkRequestTo(address(s_oracle), req, payment);
  }

  /**
   * @notice Creates a Chainlink request to the specified oracle address
   * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to
   * send LINK which creates a request on the target oracle contract.
   * Emits ChainlinkRequested event.
   * @param oracleAddress The address of the oracle for the request
   * @param req The initialized Chainlink Request
   * @param payment The amount of LINK to send for the request
   * @return requestId The request ID
   */
  function sendChainlinkRequestTo(
    address oracleAddress,
    Chainlink.Request memory req,
    uint256 payment
  ) internal returns (bytes32 requestId) {
    uint256 nonce = s_requestCount;
    s_requestCount = nonce + 1;
    bytes memory encodedRequest = abi.encodeWithSelector(
      ChainlinkRequestInterface.oracleRequest.selector,
      SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address
      AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent
      req.id,
      address(this),
      req.callbackFunctionId,
      nonce,
      ORACLE_ARGS_VERSION,
      req.buf.buf
    );
    return _rawRequest(oracleAddress, nonce, payment, encodedRequest);
  }

  /**
   * @notice Creates a Chainlink request to the stored oracle address
   * @dev This function supports multi-word response
   * @dev Calls `sendOperatorRequestTo` with the stored oracle address
   * @param req The initialized Chainlink Request
   * @param payment The amount of LINK to send for the request
   * @return requestId The request ID
   */
  function sendOperatorRequest(Chainlink.Request memory req, uint256 payment) internal returns (bytes32) {
    return sendOperatorRequestTo(address(s_oracle), req, payment);
  }

  /**
   * @notice Creates a Chainlink request to the specified oracle address
   * @dev This function supports multi-word response
   * @dev Generates and stores a request ID, increments the local nonce, and uses `transferAndCall` to
   * send LINK which creates a request on the target oracle contract.
   * Emits ChainlinkRequested event.
   * @param oracleAddress The address of the oracle for the request
   * @param req The initialized Chainlink Request
   * @param payment The amount of LINK to send for the request
   * @return requestId The request ID
   */
  function sendOperatorRequestTo(
    address oracleAddress,
    Chainlink.Request memory req,
    uint256 payment
  ) internal returns (bytes32 requestId) {
    uint256 nonce = s_requestCount;
    s_requestCount = nonce + 1;
    bytes memory encodedRequest = abi.encodeWithSelector(
      OperatorInterface.operatorRequest.selector,
      SENDER_OVERRIDE, // Sender value - overridden by onTokenTransfer by the requesting contract's address
      AMOUNT_OVERRIDE, // Amount value - overridden by onTokenTransfer by the actual amount of LINK sent
      req.id,
      req.callbackFunctionId,
      nonce,
      OPERATOR_ARGS_VERSION,
      req.buf.buf
    );
    return _rawRequest(oracleAddress, nonce, payment, encodedRequest);
  }

  /**
   * @notice Make a request to an oracle
   * @param oracleAddress The address of the oracle for the request
   * @param nonce used to generate the request ID
   * @param payment The amount of LINK to send for the request
   * @param encodedRequest data encoded for request type specific format
   * @return requestId The request ID
   */
  function _rawRequest(
    address oracleAddress,
    uint256 nonce,
    uint256 payment,
    bytes memory encodedRequest
  ) private returns (bytes32 requestId) {
    requestId = keccak256(abi.encodePacked(this, nonce));
    s_pendingRequests[requestId] = oracleAddress;
    emit ChainlinkRequested(requestId);
    require(s_link.transferAndCall(oracleAddress, payment, encodedRequest), "unable to transferAndCall to oracle");
  }

  /**
   * @notice Allows a request to be cancelled if it has not been fulfilled
   * @dev Requires keeping track of the expiration value emitted from the oracle contract.
   * Deletes the request from the `pendingRequests` mapping.
   * Emits ChainlinkCancelled event.
   * @param requestId The request ID
   * @param payment The amount of LINK sent for the request
   * @param callbackFunc The callback function specified for the request
   * @param expiration The time of the expiration for the request
   */
  function cancelChainlinkRequest(
    bytes32 requestId,
    uint256 payment,
    bytes4 callbackFunc,
    uint256 expiration
  ) internal {
    OperatorInterface requested = OperatorInterface(s_pendingRequests[requestId]);
    delete s_pendingRequests[requestId];
    emit ChainlinkCancelled(requestId);
    requested.cancelOracleRequest(requestId, payment, callbackFunc, expiration);
  }

  /**
   * @notice the next request count to be used in generating a nonce
   * @dev starts at 1 in order to ensure consistent gas cost
   * @return returns the next request count to be used in a nonce
   */
  function getNextRequestCount() internal view returns (uint256) {
    return s_requestCount;
  }

  /**
   * @notice Sets the stored oracle address
   * @param oracleAddress The address of the oracle contract
   */
  function setChainlinkOracle(address oracleAddress) internal {
    s_oracle = OperatorInterface(oracleAddress);
  }

  /**
   * @notice Sets the LINK token address
   * @param linkAddress The address of the LINK token contract
   */
  function setChainlinkToken(address linkAddress) internal {
    s_link = LinkTokenInterface(linkAddress);
  }

  /**
   * @notice Sets the Chainlink token address for the public
   * network as given by the Pointer contract
   */
  function setPublicChainlinkToken() internal {
    setChainlinkToken(PointerInterface(LINK_TOKEN_POINTER).getAddress());
  }

  /**
   * @notice Retrieves the stored address of the LINK token
   * @return The address of the LINK token
   */
  function chainlinkTokenAddress() internal view returns (address) {
    return address(s_link);
  }

  /**
   * @notice Retrieves the stored address of the oracle contract
   * @return The address of the oracle contract
   */
  function chainlinkOracleAddress() internal view returns (address) {
    return address(s_oracle);
  }

  /**
   * @notice Allows for a request which was created on another contract to be fulfilled
   * on this contract
   * @param oracleAddress The address of the oracle contract that will fulfill the request
   * @param requestId The request ID used for the response
   */
  function addChainlinkExternalRequest(address oracleAddress, bytes32 requestId) internal notPendingRequest(requestId) {
    s_pendingRequests[requestId] = oracleAddress;
  }

  /**
   * @notice Sets the stored oracle and LINK token contracts with the addresses resolved by ENS
   * @dev Accounts for subnodes having different resolvers
   * @param ensAddress The address of the ENS contract
   * @param node The ENS node hash
   */
  function useChainlinkWithENS(address ensAddress, bytes32 node) internal {
    s_ens = ENSInterface(ensAddress);
    s_ensNode = node;
    bytes32 linkSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_TOKEN_SUBNAME));
    ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(linkSubnode));
    setChainlinkToken(resolver.addr(linkSubnode));
    updateChainlinkOracleWithENS();
  }

  /**
   * @notice Sets the stored oracle contract with the address resolved by ENS
   * @dev This may be called on its own as long as `useChainlinkWithENS` has been called previously
   */
  function updateChainlinkOracleWithENS() internal {
    bytes32 oracleSubnode = keccak256(abi.encodePacked(s_ensNode, ENS_ORACLE_SUBNAME));
    ENSResolver_Chainlink resolver = ENSResolver_Chainlink(s_ens.resolver(oracleSubnode));
    setChainlinkOracle(resolver.addr(oracleSubnode));
  }

  /**
   * @notice Ensures that the fulfillment is valid for this contract
   * @dev Use if the contract developer prefers methods instead of modifiers for validation
   * @param requestId The request ID for fulfillment
   */
  function validateChainlinkCallback(bytes32 requestId)
    internal
    recordChainlinkFulfillment(requestId)
  // solhint-disable-next-line no-empty-blocks
  {

  }

  /**
   * @dev Reverts if the sender is not the oracle of the request.
   * Emits ChainlinkFulfilled event.
   * @param requestId The request ID for fulfillment
   */
  modifier recordChainlinkFulfillment(bytes32 requestId) {
    require(msg.sender == s_pendingRequests[requestId], "Source must be the oracle of the request");
    delete s_pendingRequests[requestId];
    emit ChainlinkFulfilled(requestId);
    _;
  }

  /**
   * @dev Reverts if the request is already pending
   * @param requestId The request ID for fulfillment
   */
  modifier notPendingRequest(bytes32 requestId) {
    require(s_pendingRequests[requestId] == address(0), "Request is already pending");
    _;
  }
}

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

interface ChainlinkRequestInterface {
  function oracleRequest(
    address sender,
    uint256 requestPrice,
    bytes32 serviceAgreementID,
    address callbackAddress,
    bytes4 callbackFunctionId,
    uint256 nonce,
    uint256 dataVersion,
    bytes calldata data
  ) external;

  function cancelOracleRequest(
    bytes32 requestId,
    uint256 payment,
    bytes4 callbackFunctionId,
    uint256 expiration
  ) external;
}

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

interface ENSInterface {
  // Logged when the owner of a node assigns a new owner to a subnode.
  event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);

  // Logged when the owner of a node transfers ownership to a new account.
  event Transfer(bytes32 indexed node, address owner);

  // Logged when the resolver for a node changes.
  event NewResolver(bytes32 indexed node, address resolver);

  // Logged when the TTL of a node changes
  event NewTTL(bytes32 indexed node, uint64 ttl);

  function setSubnodeOwner(
    bytes32 node,
    bytes32 label,
    address owner
  ) external;

  function setResolver(bytes32 node, address resolver) external;

  function setOwner(bytes32 node, address owner) external;

  function setTTL(bytes32 node, uint64 ttl) external;

  function owner(bytes32 node) external view returns (address);

  function resolver(bytes32 node) external view returns (address);

  function ttl(bytes32 node) external view returns (uint64);
}

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

interface LinkTokenInterface {
  function allowance(address owner, address spender) external view returns (uint256 remaining);

  function approve(address spender, uint256 value) external returns (bool success);

  function balanceOf(address owner) external view returns (uint256 balance);

  function decimals() external view returns (uint8 decimalPlaces);

  function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);

  function increaseApproval(address spender, uint256 subtractedValue) external;

  function name() external view returns (string memory tokenName);

  function symbol() external view returns (string memory tokenSymbol);

  function totalSupply() external view returns (uint256 totalTokensIssued);

  function transfer(address to, uint256 value) external returns (bool success);

  function transferAndCall(
    address to,
    uint256 value,
    bytes calldata data
  ) external returns (bool success);

  function transferFrom(
    address from,
    address to,
    uint256 value
  ) external returns (bool success);
}

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

import "./OracleInterface.sol";
import "./ChainlinkRequestInterface.sol";

interface OperatorInterface is OracleInterface, ChainlinkRequestInterface {
  function operatorRequest(
    address sender,
    uint256 payment,
    bytes32 specId,
    bytes4 callbackFunctionId,
    uint256 nonce,
    uint256 dataVersion,
    bytes calldata data
  ) external;

  function fulfillOracleRequest2(
    bytes32 requestId,
    uint256 payment,
    address callbackAddress,
    bytes4 callbackFunctionId,
    uint256 expiration,
    bytes calldata data
  ) external returns (bool);

  function ownerTransferAndCall(
    address to,
    uint256 value,
    bytes calldata data
  ) external returns (bool success);

  function distributeFunds(address payable[] calldata receivers, uint256[] calldata amounts) external payable;

  function getAuthorizedSenders() external returns (address[] memory);

  function setAuthorizedSenders(address[] calldata senders) external;

  function getForwarder() external returns (address);
}

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

interface OracleInterface {
  function fulfillOracleRequest(
    bytes32 requestId,
    uint256 payment,
    address callbackAddress,
    bytes4 callbackFunctionId,
    uint256 expiration,
    bytes32 data
  ) external returns (bool);

  function isAuthorizedSender(address node) external view returns (bool);

  function withdraw(address recipient, uint256 amount) external;

  function withdrawable() external view returns (uint256);
}

File 9 of 30 : PointerInterface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface PointerInterface {
  function getAddress() external view returns (address);
}

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

/**
 * @dev A library for working with mutable byte buffers in Solidity.
 *
 * Byte buffers are mutable and expandable, and provide a variety of primitives
 * for writing to them. At any time you can fetch a bytes object containing the
 * current contents of the buffer. The bytes object should not be stored between
 * operations, as it may change due to resizing of the buffer.
 */
library BufferChainlink {
  /**
   * @dev Represents a mutable buffer. Buffers have a current value (buf) and
   *      a capacity. The capacity may be longer than the current value, in
   *      which case it can be extended without the need to allocate more memory.
   */
  struct buffer {
    bytes buf;
    uint256 capacity;
  }

  /**
   * @dev Initializes a buffer with an initial capacity.
   * @param buf The buffer to initialize.
   * @param capacity The number of bytes of space to allocate the buffer.
   * @return The buffer, for chaining.
   */
  function init(buffer memory buf, uint256 capacity) internal pure returns (buffer memory) {
    if (capacity % 32 != 0) {
      capacity += 32 - (capacity % 32);
    }
    // Allocate space for the buffer data
    buf.capacity = capacity;
    assembly {
      let ptr := mload(0x40)
      mstore(buf, ptr)
      mstore(ptr, 0)
      mstore(0x40, add(32, add(ptr, capacity)))
    }
    return buf;
  }

  /**
   * @dev Initializes a new buffer from an existing bytes object.
   *      Changes to the buffer may mutate the original value.
   * @param b The bytes object to initialize the buffer with.
   * @return A new buffer.
   */
  function fromBytes(bytes memory b) internal pure returns (buffer memory) {
    buffer memory buf;
    buf.buf = b;
    buf.capacity = b.length;
    return buf;
  }

  function resize(buffer memory buf, uint256 capacity) private pure {
    bytes memory oldbuf = buf.buf;
    init(buf, capacity);
    append(buf, oldbuf);
  }

  function max(uint256 a, uint256 b) private pure returns (uint256) {
    if (a > b) {
      return a;
    }
    return b;
  }

  /**
   * @dev Sets buffer length to 0.
   * @param buf The buffer to truncate.
   * @return The original buffer, for chaining..
   */
  function truncate(buffer memory buf) internal pure returns (buffer memory) {
    assembly {
      let bufptr := mload(buf)
      mstore(bufptr, 0)
    }
    return buf;
  }

  /**
   * @dev Writes a byte string to a buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The start offset to write to.
   * @param data The data to append.
   * @param len The number of bytes to copy.
   * @return The original buffer, for chaining.
   */
  function write(
    buffer memory buf,
    uint256 off,
    bytes memory data,
    uint256 len
  ) internal pure returns (buffer memory) {
    require(len <= data.length);

    if (off + len > buf.capacity) {
      resize(buf, max(buf.capacity, len + off) * 2);
    }

    uint256 dest;
    uint256 src;
    assembly {
      // Memory address of the buffer data
      let bufptr := mload(buf)
      // Length of existing buffer data
      let buflen := mload(bufptr)
      // Start address = buffer address + offset + sizeof(buffer length)
      dest := add(add(bufptr, 32), off)
      // Update buffer length if we're extending it
      if gt(add(len, off), buflen) {
        mstore(bufptr, add(len, off))
      }
      src := add(data, 32)
    }

    // Copy word-length chunks while possible
    for (; len >= 32; len -= 32) {
      assembly {
        mstore(dest, mload(src))
      }
      dest += 32;
      src += 32;
    }

    // Copy remaining bytes
    unchecked {
      uint256 mask = (256**(32 - len)) - 1;
      assembly {
        let srcpart := and(mload(src), not(mask))
        let destpart := and(mload(dest), mask)
        mstore(dest, or(destpart, srcpart))
      }
    }

    return buf;
  }

  /**
   * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @param len The number of bytes to copy.
   * @return The original buffer, for chaining.
   */
  function append(
    buffer memory buf,
    bytes memory data,
    uint256 len
  ) internal pure returns (buffer memory) {
    return write(buf, buf.buf.length, data, len);
  }

  /**
   * @dev Appends a byte string to a buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) {
    return write(buf, buf.buf.length, data, data.length);
  }

  /**
   * @dev Writes a byte to the buffer. Resizes if doing so would exceed the
   *      capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The offset to write the byte at.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function writeUint8(
    buffer memory buf,
    uint256 off,
    uint8 data
  ) internal pure returns (buffer memory) {
    if (off >= buf.capacity) {
      resize(buf, buf.capacity * 2);
    }

    assembly {
      // Memory address of the buffer data
      let bufptr := mload(buf)
      // Length of existing buffer data
      let buflen := mload(bufptr)
      // Address = buffer address + sizeof(buffer length) + off
      let dest := add(add(bufptr, off), 32)
      mstore8(dest, data)
      // Update buffer length if we extended it
      if eq(off, buflen) {
        mstore(bufptr, add(buflen, 1))
      }
    }
    return buf;
  }

  /**
   * @dev Appends a byte to the buffer. Resizes if doing so would exceed the
   *      capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function appendUint8(buffer memory buf, uint8 data) internal pure returns (buffer memory) {
    return writeUint8(buf, buf.buf.length, data);
  }

  /**
   * @dev Writes up to 32 bytes to the buffer. Resizes if doing so would
   *      exceed the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The offset to write at.
   * @param data The data to append.
   * @param len The number of bytes to write (left-aligned).
   * @return The original buffer, for chaining.
   */
  function write(
    buffer memory buf,
    uint256 off,
    bytes32 data,
    uint256 len
  ) private pure returns (buffer memory) {
    if (len + off > buf.capacity) {
      resize(buf, (len + off) * 2);
    }

    unchecked {
      uint256 mask = (256**len) - 1;
      // Right-align data
      data = data >> (8 * (32 - len));
      assembly {
        // Memory address of the buffer data
        let bufptr := mload(buf)
        // Address = buffer address + sizeof(buffer length) + off + len
        let dest := add(add(bufptr, off), len)
        mstore(dest, or(and(mload(dest), not(mask)), data))
        // Update buffer length if we extended it
        if gt(add(off, len), mload(bufptr)) {
          mstore(bufptr, add(off, len))
        }
      }
    }
    return buf;
  }

  /**
   * @dev Writes a bytes20 to the buffer. Resizes if doing so would exceed the
   *      capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The offset to write at.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function writeBytes20(
    buffer memory buf,
    uint256 off,
    bytes20 data
  ) internal pure returns (buffer memory) {
    return write(buf, off, bytes32(data), 20);
  }

  /**
   * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer, for chhaining.
   */
  function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) {
    return write(buf, buf.buf.length, bytes32(data), 20);
  }

  /**
   * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer, for chaining.
   */
  function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) {
    return write(buf, buf.buf.length, data, 32);
  }

  /**
   * @dev Writes an integer to the buffer. Resizes if doing so would exceed
   *      the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param off The offset to write at.
   * @param data The data to append.
   * @param len The number of bytes to write (right-aligned).
   * @return The original buffer, for chaining.
   */
  function writeInt(
    buffer memory buf,
    uint256 off,
    uint256 data,
    uint256 len
  ) private pure returns (buffer memory) {
    if (len + off > buf.capacity) {
      resize(buf, (len + off) * 2);
    }

    uint256 mask = (256**len) - 1;
    assembly {
      // Memory address of the buffer data
      let bufptr := mload(buf)
      // Address = buffer address + off + sizeof(buffer length) + len
      let dest := add(add(bufptr, off), len)
      mstore(dest, or(and(mload(dest), not(mask)), data))
      // Update buffer length if we extended it
      if gt(add(off, len), mload(bufptr)) {
        mstore(bufptr, add(off, len))
      }
    }
    return buf;
  }

  /**
   * @dev Appends a byte to the end of the buffer. Resizes if doing so would
   * exceed the capacity of the buffer.
   * @param buf The buffer to append to.
   * @param data The data to append.
   * @return The original buffer.
   */
  function appendInt(
    buffer memory buf,
    uint256 data,
    uint256 len
  ) internal pure returns (buffer memory) {
    return writeInt(buf, buf.buf.length, data, len);
  }
}

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

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

library CBORChainlink {
  using BufferChainlink for BufferChainlink.buffer;

  uint8 private constant MAJOR_TYPE_INT = 0;
  uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1;
  uint8 private constant MAJOR_TYPE_BYTES = 2;
  uint8 private constant MAJOR_TYPE_STRING = 3;
  uint8 private constant MAJOR_TYPE_ARRAY = 4;
  uint8 private constant MAJOR_TYPE_MAP = 5;
  uint8 private constant MAJOR_TYPE_TAG = 6;
  uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7;

  uint8 private constant TAG_TYPE_BIGNUM = 2;
  uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3;

  function encodeFixedNumeric(BufferChainlink.buffer memory buf, uint8 major, uint64 value) private pure {
    if(value <= 23) {
      buf.appendUint8(uint8((major << 5) | value));
    } else if (value <= 0xFF) {
      buf.appendUint8(uint8((major << 5) | 24));
      buf.appendInt(value, 1);
    } else if (value <= 0xFFFF) {
      buf.appendUint8(uint8((major << 5) | 25));
      buf.appendInt(value, 2);
    } else if (value <= 0xFFFFFFFF) {
      buf.appendUint8(uint8((major << 5) | 26));
      buf.appendInt(value, 4);
    } else {
      buf.appendUint8(uint8((major << 5) | 27));
      buf.appendInt(value, 8);
    }
  }

  function encodeIndefiniteLengthType(BufferChainlink.buffer memory buf, uint8 major) private pure {
    buf.appendUint8(uint8((major << 5) | 31));
  }

  function encodeUInt(BufferChainlink.buffer memory buf, uint value) internal pure {
    if(value > 0xFFFFFFFFFFFFFFFF) {
      encodeBigNum(buf, value);
    } else {
      encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value));
    }
  }

  function encodeInt(BufferChainlink.buffer memory buf, int value) internal pure {
    if(value < -0x10000000000000000) {
      encodeSignedBigNum(buf, value);
    } else if(value > 0xFFFFFFFFFFFFFFFF) {
      encodeBigNum(buf, uint(value));
    } else if(value >= 0) {
      encodeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(uint256(value)));
    } else {
      encodeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(uint256(-1 - value)));
    }
  }

  function encodeBytes(BufferChainlink.buffer memory buf, bytes memory value) internal pure {
    encodeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length));
    buf.append(value);
  }

  function encodeBigNum(BufferChainlink.buffer memory buf, uint value) internal pure {
    buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM));
    encodeBytes(buf, abi.encode(value));
  }

  function encodeSignedBigNum(BufferChainlink.buffer memory buf, int input) internal pure {
    buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM));
    encodeBytes(buf, abi.encode(uint256(-1 - input)));
  }

  function encodeString(BufferChainlink.buffer memory buf, string memory value) internal pure {
    encodeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length));
    buf.append(bytes(value));
  }

  function startArray(BufferChainlink.buffer memory buf) internal pure {
    encodeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY);
  }

  function startMap(BufferChainlink.buffer memory buf) internal pure {
    encodeIndefiniteLengthType(buf, MAJOR_TYPE_MAP);
  }

  function endSequence(BufferChainlink.buffer memory buf) internal pure {
    encodeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE);
  }
}

File 12 of 30 : ENSResolver.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract ENSResolver {
  function addr(bytes32 node) public view virtual returns (address);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

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

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

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (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;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

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

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.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 Pausable is Context {
    bool private _paused;

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

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

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

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

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

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

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

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

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

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

import "./IProxyBetting.sol";

interface IFreeBetsHolder is IProxyBetting {
    function confirmLiveTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount, address _collateral) external;
    function confirmSGPTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount, address _collateral) external;

    function balancePerUserAndCollateral(address user, address collateral) external view returns (uint);
    function freeBetExpiration(address user, address collateral) external view returns (uint);
    function freeBetExpirationUpgrade() external view returns (uint);
    function freeBetExpirationPeriod() external view returns (uint);
}

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

interface ILiveTradingProcessor {
    struct LiveTradeData {
        string _gameId;
        uint16 _sportId;
        uint16 _typeId;
        int24 _line;
        uint8 _position;
        uint _buyInAmount;
        uint _expectedQuote;
        uint _additionalSlippage;
        address _referrer;
        address _collateral;
        uint24 _playerId; // 🆕 added for player props
    }

    function maxAllowedExecutionDelay() external view returns (uint);

    function requestCounter() external view returns (uint);

    function counterToRequestId(uint _counter) external view returns (bytes32);

    function requestIdToRequester(bytes32 _requestId) external view returns (address);

    function requestIdToTicketId(bytes32 _requestId) external view returns (address);

    function requestIdFulfilled(bytes32 _requestId) external view returns (bool);

    function timestampPerRequest(bytes32 _requestId) external view returns (uint);

    function getTradeData(bytes32 _requestId) external view returns (LiveTradeData memory);

    function fulfillLiveTrade(bytes32 _requestId, bool allow, uint approvedAmount) external;

    function requestLiveTrade(LiveTradeData calldata _liveTradeData) external returns (bytes32);
}

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

interface IProxyBetting {
    function getActiveTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);
    function numOfActiveTicketsPerUser(address _user) external view returns (uint);
    function getResolvedTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);
    function numOfResolvedTicketsPerUser(address _user) external view returns (uint);

    function confirmTicketResolved(address _resolvedTicket) external;
}

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/ISportsAMMV2Manager.sol";
import "../interfaces/ISportsAMMV2ResultManager.sol";
import "../interfaces/ISportsAMMV2RiskManager.sol";
import "../interfaces/ISportsAMMV2Manager.sol";
import "../interfaces/IFreeBetsHolder.sol";
import "../interfaces/IStakingThalesBettingProxy.sol";

interface ISportsAMMV2 {
    enum TicketAction {
        Exercise,
        Cancel,
        MarkLost
    }

    struct CombinedPosition {
        uint16 typeId;
        uint8 position;
        int24 line;
    }

    struct TradeData {
        bytes32 gameId;
        uint16 sportId;
        uint16 typeId;
        uint maturity;
        uint8 status;
        int24 line;
        uint24 playerId;
        uint[] odds;
        bytes32[] merkleProof;
        uint8 position;
        CombinedPosition[][] combinedPositions;
    }

    function defaultCollateral() external view returns (IERC20);

    function manager() external view returns (ISportsAMMV2Manager);

    function resultManager() external view returns (ISportsAMMV2ResultManager);

    function safeBoxFee() external view returns (uint);

    function handleTicketResolving(address _ticket, ISportsAMMV2.TicketAction action) external;

    function riskManager() external view returns (ISportsAMMV2RiskManager);

    function freeBetsHolder() external view returns (IFreeBetsHolder);

    function stakingThalesBettingProxy() external view returns (IStakingThalesBettingProxy);

    function tradeLive(
        TradeData[] calldata _tradeData,
        uint _buyInAmount,
        uint _expectedQuote,
        address _recipient,
        address _referrer,
        address _collateral
    ) external returns (address _createdTicket);

    function trade(
        TradeData[] calldata _tradeData,
        uint _buyInAmount,
        uint _expectedQuote,
        uint _additionalSlippage,
        address _referrer,
        address _collateral,
        bool _isEth
    ) external returns (address _createdTicket);

    function tradeSystemBet(
        TradeData[] calldata _tradeData,
        uint _buyInAmount,
        uint _expectedQuote,
        uint _additionalSlippage,
        address _referrer,
        address _collateral,
        bool _isEth,
        uint8 _systemBetDenominator
    ) external returns (address _createdTicket);

    function tradeSGP(
        ISportsAMMV2.TradeData[] calldata _tradeData,
        uint _buyInAmount,
        uint _approvedQuote,
        address _recipient,
        address _referrer,
        address _collateral
    ) external returns (address _createdTicket);

    function rootPerGame(bytes32 game) external view returns (bytes32);

    function getRootsPerGames(bytes32[] calldata _games) external view returns (bytes32[] memory _roots);

    function paused() external view returns (bool);
}

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

import "./ISportsAMMV2.sol";

interface ISportsAMMV2Manager {
    enum Role {
        ROOT_SETTING,
        RISK_MANAGING,
        MARKET_RESOLVING,
        TICKET_PAUSER
    }

    function isWhitelistedAddress(address _address, Role role) external view returns (bool);

    function decimals() external view returns (uint);

    function feeToken() external view returns (address);

    function isActiveTicket(address _ticket) external view returns (bool);

    function getActiveTickets(uint _index, uint _pageSize) external view returns (address[] memory);

    function numOfActiveTickets() external view returns (uint);

    function getActiveTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);

    function numOfActiveTicketsPerUser(address _user) external view returns (uint);

    function getResolvedTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);

    function numOfResolvedTicketsPerUser(address _user) external view returns (uint);

    function getTicketsPerGame(uint _index, uint _pageSize, bytes32 _gameId) external view returns (address[] memory);

    function numOfTicketsPerGame(bytes32 _gameId) external view returns (uint);

    function isKnownTicket(address _ticket) external view returns (bool);

    function sportsAMM() external view returns (address);

    function getTicketsPerMarket(
        uint _index,
        uint _pageSize,
        bytes32 _gameId,
        uint _typeId,
        uint _playerId
    ) external view returns (address[] memory);

    function numOfTicketsPerMarket(bytes32 _gameId, uint _typeId, uint _playerId) external view returns (uint);

    function addNewKnownTicket(ISportsAMMV2.TradeData[] memory _tradeData, address ticket, address user) external;

    function resolveKnownTicket(address ticket, address ticketOwner) external;

    function expireKnownTicket(address ticket, address ticketOwner) external;

    function isSystemTicket(address _ticket) external view returns (bool);

    function isSGPTicket(address _ticket) external view returns (bool);
}

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ISportsAMMV2.sol";

interface ISportsAMMV2ResultManager {
    enum MarketPositionStatus {
        Open,
        Cancelled,
        Winning,
        Losing
    }

    function isMarketResolved(
        bytes32 _gameId,
        uint16 _typeId,
        uint24 _playerId,
        int24 _line,
        ISportsAMMV2.CombinedPosition[] memory combinedPositions
    ) external view returns (bool isResolved);

    function getMarketPositionStatus(
        bytes32 _gameId,
        uint16 _typeId,
        uint24 _playerId,
        int24 _line,
        uint _position,
        ISportsAMMV2.CombinedPosition[] memory _combinedPositions
    ) external view returns (MarketPositionStatus status);

    function isWinningMarketPosition(
        bytes32 _gameId,
        uint16 _typeId,
        uint24 _playerId,
        int24 _line,
        uint _position,
        ISportsAMMV2.CombinedPosition[] memory _combinedPositions
    ) external view returns (bool isWinning);

    function isCancelledMarketPosition(
        bytes32 _gameId,
        uint16 _typeId,
        uint24 _playerId,
        int24 _line,
        uint _position,
        ISportsAMMV2.CombinedPosition[] memory _combinedPositions
    ) external view returns (bool isCancelled);

    function getResultsPerMarket(
        bytes32 _gameId,
        uint16 _typeId,
        uint24 _playerId
    ) external view returns (int24[] memory results);

    function resultTypePerMarketType(uint _typeId) external view returns (uint8 marketType);

    function isMarketResolvedAndPositionWinning(
        bytes32 _gameId,
        uint16 _typeId,
        uint24 _playerId,
        int24 _line,
        uint _position,
        ISportsAMMV2.CombinedPosition[] memory _combinedPositions
    ) external view returns (bool isResolved, bool isWinning);

    function setResultsPerMarkets(
        bytes32[] memory _gameIds,
        uint16[] memory _typeIds,
        uint24[] memory _playerIds,
        int24[][] memory _results
    ) external;

    function isGameCancelled(bytes32 _gameId) external view returns (bool);

    function cancelGames(bytes32[] memory _gameIds) external;

    function cancelMarkets(
        bytes32[] memory _gameIds,
        uint16[] memory _typeIds,
        uint24[] memory _playerIds,
        int24[] memory _lines
    ) external;

    function cancelMarket(bytes32 _gameId, uint16 _typeId, uint24 _playerId, int24 _line) external;

    function cancelGame(bytes32 _gameId) external;
}

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

import "./ISportsAMMV2.sol";

interface ISportsAMMV2RiskManager {
    struct TypeCap {
        uint typeId;
        uint cap;
    }

    struct CapData {
        uint capPerSport;
        uint capPerChild;
        TypeCap[] capPerType;
    }

    struct DynamicLiquidityData {
        uint cutoffTimePerSport;
        uint cutoffDividerPerSport;
    }

    struct RiskData {
        uint sportId;
        CapData capData;
        uint riskMultiplierPerSport;
        DynamicLiquidityData dynamicLiquidityData;
    }

    enum RiskStatus {
        NoRisk,
        OutOfLiquidity,
        InvalidCombination
    }

    function minBuyInAmount() external view returns (uint);

    function maxTicketSize() external view returns (uint);

    function maxSupportedAmount() external view returns (uint);

    function maxSupportedOdds() external view returns (uint);

    function maxAllowedSystemCombinations() external view returns (uint);

    function expiryDuration() external view returns (uint);

    function liveTradingPerSportAndTypeEnabled(uint _sportId, uint _typeId) external view returns (bool _enabled);

    function calculateCapToBeUsed(
        bytes32 _gameId,
        uint16 _sportId,
        uint16 _typeId,
        uint24 _playerId,
        int24 _line,
        uint _maturity,
        bool _isLive
    ) external view returns (uint cap);

    function calculateTotalRiskOnGame(
        bytes32 _gameId,
        uint16 _sportId,
        uint _maturity
    ) external view returns (uint totalRisk);

    function checkRisks(
        ISportsAMMV2.TradeData[] memory _tradeData,
        uint _buyInAmount,
        bool _isLive,
        uint8 _systemBetDenominator
    ) external view returns (ISportsAMMV2RiskManager.RiskStatus riskStatus, bool[] memory isMarketOutOfLiquidity);

    function checkLimits(
        uint _buyInAmount,
        uint _totalQuote,
        uint _payout,
        uint _expectedPayout,
        uint _additionalSlippage,
        uint _ticketSize
    ) external view;

    function spentOnGame(bytes32 _gameId) external view returns (uint);

    function riskPerMarketTypeAndPosition(
        bytes32 _gameId,
        uint _typeId,
        uint _playerId,
        uint _position
    ) external view returns (int);

    function checkAndUpdateRisks(
        ISportsAMMV2.TradeData[] memory _tradeData,
        uint _buyInAmount,
        uint _payout,
        bool _isLive,
        uint8 _systemBetDenominator,
        bool _isSGP
    ) external;

    function verifyMerkleTree(ISportsAMMV2.TradeData memory _marketTradeData, bytes32 _rootPerGame) external pure;

    function batchVerifyMerkleTree(
        ISportsAMMV2.TradeData[] memory _marketTradeData,
        bytes32[] memory _rootPerGame
    ) external pure;

    function isSportIdFuture(uint16 _sportsId) external view returns (bool);

    function sgpOnSportIdEnabled(uint16 _sportsId) external view returns (bool);

    function getMaxSystemBetPayout(
        ISportsAMMV2.TradeData[] memory _tradeData,
        uint8 _systemBetDenominator,
        uint _buyInAmount,
        uint _addedPayoutPercentage
    ) external view returns (uint systemBetPayout, uint systemBetQuote);

    function generateCombinations(uint8 n, uint8 k) external pure returns (uint8[][] memory);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IProxyBetting.sol";

interface IStakingThalesBettingProxy is IProxyBetting {
    function preConfirmLiveTrade(bytes32 requestId, uint _buyInAmount) external;
    function confirmLiveTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount) external;
    function preConfirmSGPTrade(bytes32 requestId, uint _buyInAmount) external;
    function confirmSGPTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount) external;
}

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

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_link","type":"address"},{"internalType":"address","name":"_oracle","type":"address"},{"internalType":"address","name":"_sportsAMM","type":"address"},{"internalType":"bytes32","name":"_jobSpecId","type":"bytes32"},{"internalType":"uint256","name":"_paymentAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"StringsInsufficientHexLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"ChainlinkCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"ChainlinkFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"ChainlinkRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_link","type":"address"},{"indexed":false,"internalType":"address","name":"_oracle","type":"address"},{"indexed":false,"internalType":"address","name":"_sportsAMM","type":"address"},{"indexed":false,"internalType":"bytes32","name":"_jobSpecId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_paymentAmount","type":"uint256"}],"name":"ContextReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"_allow","type":"bool"},{"indexed":false,"internalType":"bytes32","name":"_gameId","type":"bytes32"},{"indexed":false,"internalType":"uint16","name":"_sportId","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"_typeId","type":"uint16"},{"indexed":false,"internalType":"int24","name":"_line","type":"int24"},{"indexed":false,"internalType":"uint8","name":"_position","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_expectedQuote","type":"uint256"},{"indexed":false,"internalType":"address","name":"_collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"LiveTradeFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestCounter","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"_gameId","type":"bytes32"},{"indexed":false,"internalType":"uint16","name":"_sportId","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"_typeId","type":"uint16"},{"indexed":false,"internalType":"int24","name":"_line","type":"int24"},{"indexed":false,"internalType":"uint8","name":"_position","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_expectedQuote","type":"uint256"},{"indexed":false,"internalType":"address","name":"_collateral","type":"address"}],"name":"LiveTradeRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_freeBetsHolder","type":"address"}],"name":"SetFreeBetsHolder","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_maxAllowedExecutionDelay","type":"uint256"}],"name":"SetMaxAllowedExecutionDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_stakingThalesBettingProxy","type":"address"}],"name":"SetStakingThalesBettingProxy","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"counterToRequestId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_requestId","type":"bytes32"},{"internalType":"bool","name":"_allow","type":"bool"},{"internalType":"uint256","name":"_approvedQuote","type":"uint256"}],"name":"fulfillLiveTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"name":"getTradeData","outputs":[{"components":[{"internalType":"string","name":"_gameId","type":"string"},{"internalType":"uint16","name":"_sportId","type":"uint16"},{"internalType":"uint16","name":"_typeId","type":"uint16"},{"internalType":"int24","name":"_line","type":"int24"},{"internalType":"uint8","name":"_position","type":"uint8"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"uint256","name":"_expectedQuote","type":"uint256"},{"internalType":"uint256","name":"_additionalSlippage","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint24","name":"_playerId","type":"uint24"}],"internalType":"struct ILiveTradingProcessor.LiveTradeData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"jobSpecId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAllowedExecutionDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paymentAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"requestIdFulfilled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"requestIdToFulfillAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"requestIdToRequester","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"requestIdToTicketId","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"requestIdToTradeData","outputs":[{"internalType":"string","name":"_gameId","type":"string"},{"internalType":"uint16","name":"_sportId","type":"uint16"},{"internalType":"uint16","name":"_typeId","type":"uint16"},{"internalType":"int24","name":"_line","type":"int24"},{"internalType":"uint8","name":"_position","type":"uint8"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"uint256","name":"_expectedQuote","type":"uint256"},{"internalType":"uint256","name":"_additionalSlippage","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint24","name":"_playerId","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"_gameId","type":"string"},{"internalType":"uint16","name":"_sportId","type":"uint16"},{"internalType":"uint16","name":"_typeId","type":"uint16"},{"internalType":"int24","name":"_line","type":"int24"},{"internalType":"uint8","name":"_position","type":"uint8"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"uint256","name":"_expectedQuote","type":"uint256"},{"internalType":"uint256","name":"_additionalSlippage","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint24","name":"_playerId","type":"uint24"}],"internalType":"struct ILiveTradingProcessor.LiveTradeData","name":"_liveTradeData","type":"tuple"}],"name":"requestLiveTrade","outputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_link","type":"address"},{"internalType":"address","name":"_oracle","type":"address"},{"internalType":"address","name":"_sportsAMM","type":"address"},{"internalType":"bytes32","name":"_jobSpecId","type":"bytes32"},{"internalType":"uint256","name":"_paymentAmount","type":"uint256"}],"name":"setConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_freeBetsHolder","type":"address"}],"name":"setFreeBetsHolder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxAllowedExecutionDelay","type":"uint256"}],"name":"setMaxAllowedExecutionDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_setPausing","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakingThalesBettingProxy","type":"address"}],"name":"setStakingThalesBettingProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sportsAMM","outputs":[{"internalType":"contract ISportsAMMV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingThalesBettingProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"timestampPerRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collateral","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040526001600455603c600b553480156200001b57600080fd5b5060405162003261380380620032618339810160408190526200003e9162000153565b33806200006557604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6200007081620000e4565b506006805460ff60a01b19169055600280546001600160a01b0319166001600160a01b038716179055600380546001600160a01b0319166001600160a01b038616179055600780546001600160a01b0319166001600160a01b039490941693909317909255600955600a5550620001b09050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b80516001600160a01b03811681146200014e57600080fd5b919050565b600080600080600060a086880312156200016c57600080fd5b620001778662000136565b9450620001876020870162000136565b9350620001976040870162000136565b6060870151608090970151959894975095949392505050565b6130a180620001c06000396000f3fe608060405234801561001057600080fd5b506004361061016e5760003560e01c80638da5cb5b116100ce578063c35905c611610087578063c35905c61461033b578063c6a85f6e14610344578063c79901f314610367578063c99252881461037a578063e27a9abc1461038d578063f1c9144f146103b7578063f2fde38b146103ca57600080fd5b80638da5cb5b146102b257806390421b7c146102c3578063973a814e146102e35780639a995047146102ec578063b099d572146102ff578063bfc69e831461031257600080fd5b8063679c4de11161012b578063679c4de1146102365780636801d9fb14610249578063715018a61461025c57806371b00be8146102645780637ff39d291461026d578063805661b01461029657806383223eb5146102a957600080fd5b806301432bf2146101735780630fe62190146101ab57806316c38b3c146101cb5780632afdebb1146101e05780635c975abb14610200578063653318f814610208575b600080fd5b610196610181366004612412565b600f6020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6013546101be906001600160a01b031681565b6040516101a2919061242b565b6101de6101d936600461244d565b6103dd565b005b6101f36101ee366004612412565b6103fd565b6040516101a291906124ba565b610196610599565b610228610216366004612412565b60106020526000908152604090205481565b6040519081526020016101a2565b6101de6102443660046125aa565b6105a9565b6101de6102573660046125e3565b610637565b6101de610695565b61022860095481565b6101be61027b366004612412565b600d602052600090815260409020546001600160a01b031681565b6101de6102a43660046125e3565b6106a9565b610228600b5481565b6006546001600160a01b03166101be565b6102286102d1366004612412565b60126020526000908152604090205481565b61022860115481565b6101de6102fa366004612412565b6106fc565b61022861030d366004612600565b610739565b6101be610320366004612412565b6014602052600090815260409020546001600160a01b031681565b610228600a5481565b610196610352366004612412565b600e6020526000908152604090205460ff1681565b6101de61037536600461263b565b610cf7565b6007546101be906001600160a01b031681565b6103a061039b366004612412565b610db4565b6040516101a29b9a99989796959493929190612696565b6101de6103c5366004612714565b610eb5565b6101de6103d83660046125e3565b611750565b6103e561178b565b806103f5576103f26117b8565b50565b6103f2611807565b6040805161016081018252606080825260006020830181905292820183905281018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091526000828152600c6020526040908190208151610160810190925280548290829061047f9061274c565b80601f01602080910402602001604051908101604052809291908181526020018280546104ab9061274c565b80156104f85780601f106104cd576101008083540402835291602001916104f8565b820191906000526020600020905b8154815290600101906020018083116104db57829003601f168201915b5050509183525050600182015461ffff80821660208401526201000082041660408301526401000000008104600290810b6060840152600160381b90910460ff16608083015282015460a0820152600382015460c0820152600482015460e082015260058201546001600160a01b03908116610100830152600690920154918216610120820152600160a01b90910462ffffff166101409091015292915050565b600654600160a01b900460ff1690565b6105b161178b565b61063381836001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016105e1919061242b565b602060405180830381865afa1580156105fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106229190612780565b6001600160a01b038516919061184a565b5050565b61063f61178b565b601380546001600160a01b0319166001600160a01b0383161790556040517f52dc607ac868d5b1fa7bba518a16fe90c6fc54562dcb05e35304ac1d26d32a249061068a90839061242b565b60405180910390a150565b61069d61178b565b6106a760006118a1565b565b6106b161178b565b600880546001600160a01b0319166001600160a01b0383161790556040517fc238ff72ce9e31f22c0ef8bf1c701fcc6afd3ed4edc2224a63f16346b68420fb9061068a90839061242b565b61070461178b565b600b8190556040518181527fa1d20d22955d911c8b338eeec58c0fa61197b85b255251efe74df475baa5c7389060200161068a565b60006107436118f3565b600760009054906101000a90046001600160a01b03166001600160a01b031663478426636040518163ffffffff1660e01b8152600401602060405180830381865afa158015610796573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ba9190612799565b6001600160a01b031663ce440b936107d860408501602086016127c6565b6107e860608601604087016127c6565b6040516001600160e01b031960e085901b16815261ffff928316600482015291166024820152604401602060405180830381865afa15801561082e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085291906127e3565b6108af5760405162461bcd60e51b8152602060048201526024808201527f4c6976652074726164696e67206e6f7420656e61626c6564206f6e205f73706f6044820152631c9d125960e21b60648201526084015b60405180910390fd5b6108b76123c3565b6009546108cc903063f1c9144f60e01b611919565b60408051808201909152600681526519d85b59525960d21b6020820152909150610938906108fa8580612800565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508694939250506119409050565b61097a604051806040016040528060078152602001661cdc1bdc9d125960ca1b81525084602001602081019061096e91906127c6565b83919061ffff1661195e565b60408051808201825260068152651d1e5c19525960d21b60208201526109aa9161096e90606087019087016127c6565b6040805180820190915260048152636c696e6560e01b60208201526109e4906109d9608086016060870161285c565b83919060020b61197c565b6040805180820190915260088152673837b9b4ba34b7b760c11b6020820152610a2290610a1760a0860160808701612888565b83919060ff1661195e565b60408051808201909152600b81526a189d5e525b905b5bdd5b9d60aa1b6020820152610a5490829060a086013561195e565b60408051808201909152600d81526c657870656374656451756f746560981b6020820152610a8890829060c086013561195e565b6040805180820190915260128152716164646974696f6e616c536c69707061676560701b6020820152610ac190829060e086013561195e565b6040805180820190915260088152671c1b185e595c925960c21b6020820152610b0390610af6610160860161014087016128b6565b83919062ffffff1661195e565b610b39604051806040016040528060098152602001683932b8bab2b9ba32b960b91b815250610b313361199a565b839190611940565b610b4581600a546119b6565b6000818152601060209081526040808320429055600c90915290209092508390610b6f8282612a58565b50506000828152600d6020908152604080832080546001600160a01b0319163390811790915560118054855260129093529220849055547fdc457bcbffcdb3995df4b3bb7a325fe68d19b75af17c94b8d1d277a85691e20f919084610c11610bd78880612800565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506119d092505050565b610c216040890160208a016127c6565b610c3160608a0160408b016127c6565b610c4160808b0160608c0161285c565b610c5160a08c0160808d01612888565b8b60a001358c60c001358d610120016020810190610c6f91906125e3565b604080516001600160a01b039c8d168152602081019b909b528a0198909852606089019690965261ffff94851660808901529390921660a087015260020b60c086015260ff1660e0850152610100840152610120830152919091166101408201526101600160405180910390a160118054906000610cec83612bb6565b919050555050919050565b610cff61178b565b600280546001600160a01b0319166001600160a01b038716179055600380546001600160a01b0319166001600160a01b038616179055600780546001600160a01b0319166001600160a01b038581169182179092556009849055600a839055604080518884168152928716602084015282015260608101839052608081018290527f4bba2924c9f8b1d7003061aed0e3737c74fddf1b79cd48799498f21058f40ef89060a00160405180910390a15050505050565b600c60205260009081526040902080548190610dcf9061274c565b80601f0160208091040260200160405190810160405280929190818152602001828054610dfb9061274c565b8015610e485780601f10610e1d57610100808354040283529160200191610e48565b820191906000526020600020905b815481529060010190602001808311610e2b57829003601f168201915b505050506001830154600280850154600386015460048701546005880154600690980154969761ffff808716986201000088049091169750640100000000870490950b95600160381b900460ff16946001600160a01b0391821691811690600160a01b900462ffffff168b565b610ebd6118f3565b60008381526005602052604090205483906001600160a01b03163314610f365760405162461bcd60e51b815260206004820152602860248201527f536f75726365206d75737420626520746865206f7261636c65206f6620746865604482015267081c995c5d595cdd60c21b60648201526084016108a6565b60008181526005602052604080822080546001600160a01b03191690555182917f7cc135e0cebb02c3480ae5d74d377283180a2601f8f644edf7987b009316c63a91a26000848152600f602052604090205460ff1615610fd85760405162461bcd60e51b815260206004820152601c60248201527f5265717565737420494420616c72656164792066756c66696c6c65640000000060448201526064016108a6565b600b546000858152601060205260409020544291610ff591612bcf565b116110365760405162461bcd60e51b815260206004820152601160248201527014995c5d595cdd081d1a5b5959081bdd5d607a1b60448201526064016108a6565b6000848152600c60205260408082208151610160810190925280548290829061105e9061274c565b80601f016020809104026020016040519081016040528092919081815260200182805461108a9061274c565b80156110d75780601f106110ac576101008083540402835291602001916110d7565b820191906000526020600020905b8154815290600101906020018083116110ba57829003601f168201915b5050509183525050600182015461ffff8082166020808501919091526201000083049091166040808501919091526401000000008304600290810b6060860152600160381b90930460ff1660808501529184015460a0840152600384015460c0840152600484015460e08085019190915260058501546001600160a01b03908116610100860152600690950154808616610120860152600160a01b900462ffffff166101409094019390935260008a8152600d90915220549083015192935016906111aa90670de0b6b3a7640000612bcf565b60c08301516111c186670de0b6b3a7640000612be2565b6111cb9190612c0f565b111561120d5760405162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b60448201526064016108a6565b841561163357604080516001808252818301909252600091816020015b60408051610160810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e083018190526101008301819052610120830191909152610140820152825260001990920191018161122a5750506040805160e1808252611c40820190925291925060609160009160208201611c20803683370190505090508681866080015160ff16815181106112d9576112d9612c23565b60209081029190910101526040805160e1808252611c408201909252600091816020015b60608152602001906001900390816112fd57905050905060405180610160016040528061132d88600001516119d0565b8152602001876020015161ffff168152602001876040015161ffff16815260200142603c61135b9190612bcf565b8152602001600060ff168152602001876060015160020b815260200187610140015162ffffff168152602001838152602001848152602001876080015160ff16815260200182815250846000815181106113b7576113b7612c23565b60209081029190910101526013546001600160a01b03908116908616036114445760135460a08701516040516305225bf960e31b8152600481018d905260248101919091526001600160a01b0390911690632912dfc890604401600060405180830381600087803b15801561142b57600080fd5b505af115801561143f573d6000803e3d6000fd5b505050505b6000600760009054906101000a90046001600160a01b03166001600160a01b031663e481a342868960a001518c8a8c61010001518d61012001516040518763ffffffff1660e01b815260040161149f96959493929190612d05565b6020604051808303816000875af11580156114be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e29190612799565b60008c815260146020526040902080546001600160a01b0319166001600160a01b0383811691909117909155600854919250908116908716036115a45760085460a088015161012089015160405163a12d5b0760e01b8152600481018f90526001600160a01b0385811660248301526044820193909352908216606482015291169063a12d5b0790608401600060405180830381600087803b15801561158757600080fd5b505af115801561159b573d6000803e3d6000fd5b5050505061162d565b6013546001600160a01b039081169087160361162d5760135460a088015160405163d9a7951f60e01b8152600481018e90526001600160a01b038481166024830152604482019290925291169063d9a7951f90606401600060405180830381600087803b15801561161457600080fd5b505af1158015611628573d6000803e3d6000fd5b505050505b50505050505b6000868152600e60209081526040808320805489151560ff1991821617909155600f9092529091208054909116600117905581517fe16fea440372e1dff8f7e8cb79dcc6e6af20f2af3a278835a2049243b25d03ca90829088908890611698906119d0565b86602001518760400151886060015189608001518a60a001518d8c6101200151426040516117409c9b9a999897969594939291906001600160a01b039c8d168152602081019b909b5298151560408b015260608a019790975261ffff95861660808a01529390941660a088015260029190910b60c087015260ff1660e08601526101008501919091526101208401529092166101408201526101608101919091526101800190565b60405180910390a1505050505050565b61175861178b565b6001600160a01b038116611782576000604051631e4fbdf760e01b81526004016108a6919061242b565b6103f2816118a1565b6006546001600160a01b031633146106a7573360405163118cdaa760e01b81526004016108a6919061242b565b6117c06119ef565b6006805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516117fd919061242b565b60405180910390a1565b61180f6118f3565b6006805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586117f03390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261189c908490611a14565b505050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6118fb610599565b156106a75760405163d93c066560e01b815260040160405180910390fd5b6119216123c3565b6119296123c3565b61193581868686611a6e565b9150505b9392505050565b608083015161194f9083611ab4565b608083015161189c9082611ab4565b608083015161196d9083611ab4565b608083015161189c9082611acb565b608083015161198b9083611ab4565b608083015161189c9082611af0565b60606119b06001600160a01b0383166014611b4f565b92915050565b600354600090611939906001600160a01b03168484611cc6565b8051600090829082036119e65750600092915050565b50506020015190565b6119f7610599565b6106a757604051638dfc202b60e01b815260040160405180910390fd5b6000611a296001600160a01b03841683611d59565b90508051600014158015611a4e575080806020019051810190611a4c91906127e3565b155b1561189c5782604051635274afe760e01b81526004016108a6919061242b565b611a766123c3565b611a868560800151610100611d67565b50508284526001600160a01b03821660208501526001600160e01b031981166040850152835b949350505050565b611ac18260038351611dbf565b61189c8282611ecc565b6001600160401b03811115611ae4576106338282611ee6565b61063382600083611dbf565b67ffffffffffffffff19811215611b0b576106338282611f1d565b6001600160401b03811315611b24576106338282611ee6565b60008112611b385761063382600083611dbf565b610633826001611b4a84600019612e62565b611dbf565b6060826000611b5f846002612be2565b611b6a906002612bcf565b6001600160401b03811115611b8157611b816128d3565b6040519080825280601f01601f191660200182016040528015611bab576020820181803683370190505b509050600360fc1b81600081518110611bc657611bc6612c23565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110611bf557611bf5612c23565b60200101906001600160f81b031916908160001a9053506000611c19856002612be2565b611c24906001612bcf565b90505b6001811115611c9c576f181899199a1a9b1b9c1cb0b131b232b360811b83600f1660108110611c5857611c58612c23565b1a60f81b828281518110611c6e57611c6e612c23565b60200101906001600160f81b031916908160001a90535060049290921c91611c9581612e89565b9050611c27565b508115611aac5760405163e22e27eb60e01b815260048101869052602481018590526044016108a6565b600454600090611cd7816001612bcf565b600455835160408086015160808701515191516000936320214ca360e11b93611d0f9386938493923092918a91600191602401612ea0565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091529050611d4f86838684611f4b565b9695505050505050565b6060611939838360006120a9565b611d6f6123f8565b611d7a602083612f08565b15611da257611d8a602083612f08565b611d95906020612f1c565b611d9f9083612bcf565b91505b506020828101829052604080518085526000815290920101905290565b6017816001600160401b031611611de957611de38360e0600585901b16831761213c565b50505050565b60ff816001600160401b031611611e2557611e0f836018611fe0600586901b161761213c565b50611de3836001600160401b0383166001612154565b61ffff816001600160401b031611611e6257611e4c836019611fe0600586901b161761213c565b50611de3836001600160401b0383166002612154565b63ffffffff816001600160401b031611611ea157611e8b83601a611fe0600586901b161761213c565b50611de3836001600160401b0383166004612154565b611eb683601b611fe0600586901b161761213c565b50611de3836001600160401b0383166008612154565b611ed46123f8565b6119398384600001515184855161216d565b611ef18260c261213c565b506106338282604051602001611f0991815260200190565b60405160208183030381529060405261224a565b611f288260c361213c565b5061063382611f3983600019612e62565b604051602001611f0991815260200190565b6040516bffffffffffffffffffffffff193060601b1660208201526034810184905260009060540160408051808303601f1901815282825280516020918201206000818152600590925291812080546001600160a01b0319166001600160a01b038a1617905590925082917fb5e6e01e79f91267dc17b4e6314d5d4d03593d2ceee0fbb452b750bd70ea5af99190a2600254604051630200057560e51b81526001600160a01b0390911690634000aea09061200e90889087908790600401612f2f565b6020604051808303816000875af115801561202d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061205191906127e3565b611aac5760405162461bcd60e51b815260206004820152602360248201527f756e61626c6520746f207472616e73666572416e6443616c6c20746f206f7261604482015262636c6560e81b60648201526084016108a6565b6060814710156120ce573060405163cd78605960e01b81526004016108a6919061242b565b600080856001600160a01b031684866040516120ea9190612f5f565b60006040518083038185875af1925050503d8060008114612127576040519150601f19603f3d011682016040523d82523d6000602084013e61212c565b606091505b5091509150611d4f868383612257565b6121446123f8565b61193983846000015151846122aa565b61215c6123f8565b611aac8485600001515185856122f8565b6121756123f8565b825182111561218357600080fd5b60208501516121928386612bcf565b11156121c5576121c5856121b5876020015187866121b09190612bcf565b61236c565b6121c0906002612be2565b612383565b6000808651805187602083010193508088870111156121e45787860182525b505050602084015b602084106122245780518252612203602083612bcf565b9150612210602082612bcf565b905061221d602085612f1c565b93506121ec565b51815160001960208690036101000a019081169019919091161790525083949350505050565b611ac18260028351611dbf565b60608261226c576122678261239a565b611939565b815115801561228357506001600160a01b0384163b155b156122a35783604051639996b31560e01b81526004016108a6919061242b565b5080611939565b6122b26123f8565b836020015183106122d2576122d284856020015160026121c09190612be2565b83518051602085830101848153508085036122ee576001810182525b5093949350505050565b6123006123f8565b602085015161230f8584612bcf565b111561232357612323856121b58685612bcf565b600060016123338461010061305f565b61233d9190612f1c565b90508551838682010185831982511617815250805184870111156123615783860181525b509495945050505050565b60008183111561237d5750816119b0565b50919050565b815161238f8383611d67565b50611de38382611ecc565b8051156123aa5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6040805160a0810182526000808252602082018190529181018290526060810191909152608081016123f36123f8565b905290565b604051806040016040528060608152602001600081525090565b60006020828403121561242457600080fd5b5035919050565b6001600160a01b0391909116815260200190565b80151581146103f257600080fd5b60006020828403121561245f57600080fd5b81356119398161243f565b60005b8381101561248557818101518382015260200161246d565b50506000910152565b600081518084526124a681602086016020860161246a565b601f01601f19169290920160200192915050565b60208152600082516101608060208501526124d961018085018361248e565b915060208501516124f0604086018261ffff169052565b50604085015161ffff81166060860152506060850151612515608086018260020b9052565b50608085015160ff811660a08601525060a085015160c085015260c085015160e085015260e0850151610100818187015280870151915050610120612564818701836001600160a01b03169052565b8601519050610140612580868201836001600160a01b03169052565b9095015162ffffff1693019290925250919050565b6001600160a01b03811681146103f257600080fd5b600080604083850312156125bd57600080fd5b82356125c881612595565b915060208301356125d881612595565b809150509250929050565b6000602082840312156125f557600080fd5b813561193981612595565b60006020828403121561261257600080fd5b81356001600160401b0381111561262857600080fd5b8201610160818503121561193957600080fd5b600080600080600060a0868803121561265357600080fd5b853561265e81612595565b9450602086013561266e81612595565b9350604086013561267e81612595565b94979396509394606081013594506080013592915050565b60006101608083526126aa8184018f61248e565b61ffff9d8e1660208501529b909c1660408301525060029890980b606089015260ff96909616608088015260a087019490945260c086019290925260e08501526001600160a01b039081166101008501521661012083015262ffffff166101409091015292915050565b60008060006060848603121561272957600080fd5b83359250602084013561273b8161243f565b929592945050506040919091013590565b600181811c9082168061276057607f821691505b60208210810361237d57634e487b7160e01b600052602260045260246000fd5b60006020828403121561279257600080fd5b5051919050565b6000602082840312156127ab57600080fd5b815161193981612595565b61ffff811681146103f257600080fd5b6000602082840312156127d857600080fd5b8135611939816127b6565b6000602082840312156127f557600080fd5b81516119398161243f565b6000808335601e1984360301811261281757600080fd5b8301803591506001600160401b0382111561283157600080fd5b60200191503681900382131561284657600080fd5b9250929050565b8060020b81146103f257600080fd5b60006020828403121561286e57600080fd5b81356119398161284d565b60ff811681146103f257600080fd5b60006020828403121561289a57600080fd5b813561193981612879565b62ffffff811681146103f257600080fd5b6000602082840312156128c857600080fd5b8135611939816128a5565b634e487b7160e01b600052604160045260246000fd5b601f82111561189c57600081815260208120601f850160051c810160208610156129105750805b601f850160051c820191505b8181101561292f5782815560010161291c565b505050505050565b6001600160401b0383111561294e5761294e6128d3565b6129628361295c835461274c565b836128e9565b6000601f841160018114612996576000851561297e5750838201355b600019600387901b1c1916600186901b1783556129f0565b600083815260209020601f19861690835b828110156129c757868501358255602094850194600190920191016129a7565b50868210156129e45760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600081356119b0816127b6565b600081356119b08161284d565b600081356119b081612879565b600081356119b081612595565b80546001600160a01b0319166001600160a01b0392909216919091179055565b600081356119b0816128a5565b612a628283612800565b612a6d818385612937565b505060018101612a95612a82602085016129f7565b825461ffff191661ffff91909116178255565b612abe612aa4604085016129f7565b825463ffff0000191660109190911b63ffff000016178255565b612aef612acd60608501612a04565b825460209190911b66ffffff000000001666ffffff0000000019909116178255565b612b1c612afe60808501612a11565b82805460ff60381b191660389290921b60ff60381b16919091179055565b5060a0820135600282015560c0820135600382015560e08201356004820155612b54612b4b6101008401612a1e565b60058301612a2b565b60068101612b6e612b686101208501612a1e565b82612a2b565b61189c612b7e6101408501612a4b565b82805462ffffff60a01b191660a09290921b62ffffff60a01b16919091179055565b634e487b7160e01b600052601160045260246000fd5b600060018201612bc857612bc8612ba0565b5060010190565b808201808211156119b0576119b0612ba0565b80820281158282048414176119b0576119b0612ba0565b634e487b7160e01b600052601260045260246000fd5b600082612c1e57612c1e612bf9565b500490565b634e487b7160e01b600052603260045260246000fd5b600081518084526020808501945080840160005b8381101561236157815187529582019590820190600101612c4d565b600081518084526020808501808196508360051b810191508286016000805b86811015612cf7578385038a52825180518087529087019087870190845b81811015612ce2578351805161ffff1684528a81015160ff168b85015260409081015160020b9084015292890192606090920191600101612ca6565b50509a87019a95505091850191600101612c88565b509298975050505050505050565b600060c0808301818452808a5180835260e092508286019150828160051b8701016020808e0160005b84811015612e105789840360df190186528151805185528381015161ffff90811685870152604080830151909116908601526060808201519086015260808082015160ff169086015260a08082015160020b908601528881015162ffffff16898601528781015161016089870181905290612dab82880182612c39565b9150506101008083015187830382890152612dc68382612c39565b9250505061012080830151612ddf8289018260ff169052565b50506101408083015192508682038188015250612dfc8183612c69565b978501979550505090820190600101612d2e565b505087018c9052604087018b90526001600160a01b038a1660608801529450612e399350505050565b6001600160a01b03841660808301526001600160a01b03831660a0830152979650505050505050565b8181036000831280158383131683831282161715612e8257612e82612ba0565b5092915050565b600081612e9857612e98612ba0565b506000190190565b6001600160a01b0389811682526020820189905260408201889052861660608201526001600160e01b03198516608082015260a0810184905260c0810183905261010060e08201819052600090612ef98382018561248e565b9b9a5050505050505050505050565b600082612f1757612f17612bf9565b500690565b818103818111156119b0576119b0612ba0565b60018060a01b0384168152826020820152606060408201526000612f56606083018461248e565b95945050505050565b60008251612f7181846020870161246a565b9190910192915050565b600181815b80851115612fb6578160001904821115612f9c57612f9c612ba0565b80851615612fa957918102915b93841c9390800290612f80565b509250929050565b600082612fcd575060016119b0565b81612fda575060006119b0565b8160018114612ff05760028114612ffa57613016565b60019150506119b0565b60ff84111561300b5761300b612ba0565b50506001821b6119b0565b5060208310610133831016604e8410600b8410161715613039575081810a6119b0565b6130438383612f7b565b806000190482111561305757613057612ba0565b029392505050565b60006119398383612fbe56fea264697066735822122085cc9998b447cff0a1e45985f1c0dd54a17800dd8d93fd8b7e184ddef8d1ff9c64736f6c63430008140033000000000000000000000000149459ad88d23d2ce0f4f1371203db2122331d94000000000000000000000000b7dec0966366ecc5a1906f83caa12cca2edb0a8c00000000000000000000000076923cdde21928ddbec4b8bfdc8143bb6d0841a864316539393636633462386434383738386335633763333962356338383532640000000000000000000000000000000000000000000000000de0b6b3a7640000

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061016e5760003560e01c80638da5cb5b116100ce578063c35905c611610087578063c35905c61461033b578063c6a85f6e14610344578063c79901f314610367578063c99252881461037a578063e27a9abc1461038d578063f1c9144f146103b7578063f2fde38b146103ca57600080fd5b80638da5cb5b146102b257806390421b7c146102c3578063973a814e146102e35780639a995047146102ec578063b099d572146102ff578063bfc69e831461031257600080fd5b8063679c4de11161012b578063679c4de1146102365780636801d9fb14610249578063715018a61461025c57806371b00be8146102645780637ff39d291461026d578063805661b01461029657806383223eb5146102a957600080fd5b806301432bf2146101735780630fe62190146101ab57806316c38b3c146101cb5780632afdebb1146101e05780635c975abb14610200578063653318f814610208575b600080fd5b610196610181366004612412565b600f6020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6013546101be906001600160a01b031681565b6040516101a2919061242b565b6101de6101d936600461244d565b6103dd565b005b6101f36101ee366004612412565b6103fd565b6040516101a291906124ba565b610196610599565b610228610216366004612412565b60106020526000908152604090205481565b6040519081526020016101a2565b6101de6102443660046125aa565b6105a9565b6101de6102573660046125e3565b610637565b6101de610695565b61022860095481565b6101be61027b366004612412565b600d602052600090815260409020546001600160a01b031681565b6101de6102a43660046125e3565b6106a9565b610228600b5481565b6006546001600160a01b03166101be565b6102286102d1366004612412565b60126020526000908152604090205481565b61022860115481565b6101de6102fa366004612412565b6106fc565b61022861030d366004612600565b610739565b6101be610320366004612412565b6014602052600090815260409020546001600160a01b031681565b610228600a5481565b610196610352366004612412565b600e6020526000908152604090205460ff1681565b6101de61037536600461263b565b610cf7565b6007546101be906001600160a01b031681565b6103a061039b366004612412565b610db4565b6040516101a29b9a99989796959493929190612696565b6101de6103c5366004612714565b610eb5565b6101de6103d83660046125e3565b611750565b6103e561178b565b806103f5576103f26117b8565b50565b6103f2611807565b6040805161016081018252606080825260006020830181905292820183905281018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091526000828152600c6020526040908190208151610160810190925280548290829061047f9061274c565b80601f01602080910402602001604051908101604052809291908181526020018280546104ab9061274c565b80156104f85780601f106104cd576101008083540402835291602001916104f8565b820191906000526020600020905b8154815290600101906020018083116104db57829003601f168201915b5050509183525050600182015461ffff80821660208401526201000082041660408301526401000000008104600290810b6060840152600160381b90910460ff16608083015282015460a0820152600382015460c0820152600482015460e082015260058201546001600160a01b03908116610100830152600690920154918216610120820152600160a01b90910462ffffff166101409091015292915050565b600654600160a01b900460ff1690565b6105b161178b565b61063381836001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016105e1919061242b565b602060405180830381865afa1580156105fe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106229190612780565b6001600160a01b038516919061184a565b5050565b61063f61178b565b601380546001600160a01b0319166001600160a01b0383161790556040517f52dc607ac868d5b1fa7bba518a16fe90c6fc54562dcb05e35304ac1d26d32a249061068a90839061242b565b60405180910390a150565b61069d61178b565b6106a760006118a1565b565b6106b161178b565b600880546001600160a01b0319166001600160a01b0383161790556040517fc238ff72ce9e31f22c0ef8bf1c701fcc6afd3ed4edc2224a63f16346b68420fb9061068a90839061242b565b61070461178b565b600b8190556040518181527fa1d20d22955d911c8b338eeec58c0fa61197b85b255251efe74df475baa5c7389060200161068a565b60006107436118f3565b600760009054906101000a90046001600160a01b03166001600160a01b031663478426636040518163ffffffff1660e01b8152600401602060405180830381865afa158015610796573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107ba9190612799565b6001600160a01b031663ce440b936107d860408501602086016127c6565b6107e860608601604087016127c6565b6040516001600160e01b031960e085901b16815261ffff928316600482015291166024820152604401602060405180830381865afa15801561082e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061085291906127e3565b6108af5760405162461bcd60e51b8152602060048201526024808201527f4c6976652074726164696e67206e6f7420656e61626c6564206f6e205f73706f6044820152631c9d125960e21b60648201526084015b60405180910390fd5b6108b76123c3565b6009546108cc903063f1c9144f60e01b611919565b60408051808201909152600681526519d85b59525960d21b6020820152909150610938906108fa8580612800565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508694939250506119409050565b61097a604051806040016040528060078152602001661cdc1bdc9d125960ca1b81525084602001602081019061096e91906127c6565b83919061ffff1661195e565b60408051808201825260068152651d1e5c19525960d21b60208201526109aa9161096e90606087019087016127c6565b6040805180820190915260048152636c696e6560e01b60208201526109e4906109d9608086016060870161285c565b83919060020b61197c565b6040805180820190915260088152673837b9b4ba34b7b760c11b6020820152610a2290610a1760a0860160808701612888565b83919060ff1661195e565b60408051808201909152600b81526a189d5e525b905b5bdd5b9d60aa1b6020820152610a5490829060a086013561195e565b60408051808201909152600d81526c657870656374656451756f746560981b6020820152610a8890829060c086013561195e565b6040805180820190915260128152716164646974696f6e616c536c69707061676560701b6020820152610ac190829060e086013561195e565b6040805180820190915260088152671c1b185e595c925960c21b6020820152610b0390610af6610160860161014087016128b6565b83919062ffffff1661195e565b610b39604051806040016040528060098152602001683932b8bab2b9ba32b960b91b815250610b313361199a565b839190611940565b610b4581600a546119b6565b6000818152601060209081526040808320429055600c90915290209092508390610b6f8282612a58565b50506000828152600d6020908152604080832080546001600160a01b0319163390811790915560118054855260129093529220849055547fdc457bcbffcdb3995df4b3bb7a325fe68d19b75af17c94b8d1d277a85691e20f919084610c11610bd78880612800565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506119d092505050565b610c216040890160208a016127c6565b610c3160608a0160408b016127c6565b610c4160808b0160608c0161285c565b610c5160a08c0160808d01612888565b8b60a001358c60c001358d610120016020810190610c6f91906125e3565b604080516001600160a01b039c8d168152602081019b909b528a0198909852606089019690965261ffff94851660808901529390921660a087015260020b60c086015260ff1660e0850152610100840152610120830152919091166101408201526101600160405180910390a160118054906000610cec83612bb6565b919050555050919050565b610cff61178b565b600280546001600160a01b0319166001600160a01b038716179055600380546001600160a01b0319166001600160a01b038616179055600780546001600160a01b0319166001600160a01b038581169182179092556009849055600a839055604080518884168152928716602084015282015260608101839052608081018290527f4bba2924c9f8b1d7003061aed0e3737c74fddf1b79cd48799498f21058f40ef89060a00160405180910390a15050505050565b600c60205260009081526040902080548190610dcf9061274c565b80601f0160208091040260200160405190810160405280929190818152602001828054610dfb9061274c565b8015610e485780601f10610e1d57610100808354040283529160200191610e48565b820191906000526020600020905b815481529060010190602001808311610e2b57829003601f168201915b505050506001830154600280850154600386015460048701546005880154600690980154969761ffff808716986201000088049091169750640100000000870490950b95600160381b900460ff16946001600160a01b0391821691811690600160a01b900462ffffff168b565b610ebd6118f3565b60008381526005602052604090205483906001600160a01b03163314610f365760405162461bcd60e51b815260206004820152602860248201527f536f75726365206d75737420626520746865206f7261636c65206f6620746865604482015267081c995c5d595cdd60c21b60648201526084016108a6565b60008181526005602052604080822080546001600160a01b03191690555182917f7cc135e0cebb02c3480ae5d74d377283180a2601f8f644edf7987b009316c63a91a26000848152600f602052604090205460ff1615610fd85760405162461bcd60e51b815260206004820152601c60248201527f5265717565737420494420616c72656164792066756c66696c6c65640000000060448201526064016108a6565b600b546000858152601060205260409020544291610ff591612bcf565b116110365760405162461bcd60e51b815260206004820152601160248201527014995c5d595cdd081d1a5b5959081bdd5d607a1b60448201526064016108a6565b6000848152600c60205260408082208151610160810190925280548290829061105e9061274c565b80601f016020809104026020016040519081016040528092919081815260200182805461108a9061274c565b80156110d75780601f106110ac576101008083540402835291602001916110d7565b820191906000526020600020905b8154815290600101906020018083116110ba57829003601f168201915b5050509183525050600182015461ffff8082166020808501919091526201000083049091166040808501919091526401000000008304600290810b6060860152600160381b90930460ff1660808501529184015460a0840152600384015460c0840152600484015460e08085019190915260058501546001600160a01b03908116610100860152600690950154808616610120860152600160a01b900462ffffff166101409094019390935260008a8152600d90915220549083015192935016906111aa90670de0b6b3a7640000612bcf565b60c08301516111c186670de0b6b3a7640000612be2565b6111cb9190612c0f565b111561120d5760405162461bcd60e51b81526020600482015260116024820152700a6d8d2e0e0c2ceca40e8dede40d0d2ced607b1b60448201526064016108a6565b841561163357604080516001808252818301909252600091816020015b60408051610160810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e083018190526101008301819052610120830191909152610140820152825260001990920191018161122a5750506040805160e1808252611c40820190925291925060609160009160208201611c20803683370190505090508681866080015160ff16815181106112d9576112d9612c23565b60209081029190910101526040805160e1808252611c408201909252600091816020015b60608152602001906001900390816112fd57905050905060405180610160016040528061132d88600001516119d0565b8152602001876020015161ffff168152602001876040015161ffff16815260200142603c61135b9190612bcf565b8152602001600060ff168152602001876060015160020b815260200187610140015162ffffff168152602001838152602001848152602001876080015160ff16815260200182815250846000815181106113b7576113b7612c23565b60209081029190910101526013546001600160a01b03908116908616036114445760135460a08701516040516305225bf960e31b8152600481018d905260248101919091526001600160a01b0390911690632912dfc890604401600060405180830381600087803b15801561142b57600080fd5b505af115801561143f573d6000803e3d6000fd5b505050505b6000600760009054906101000a90046001600160a01b03166001600160a01b031663e481a342868960a001518c8a8c61010001518d61012001516040518763ffffffff1660e01b815260040161149f96959493929190612d05565b6020604051808303816000875af11580156114be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e29190612799565b60008c815260146020526040902080546001600160a01b0319166001600160a01b0383811691909117909155600854919250908116908716036115a45760085460a088015161012089015160405163a12d5b0760e01b8152600481018f90526001600160a01b0385811660248301526044820193909352908216606482015291169063a12d5b0790608401600060405180830381600087803b15801561158757600080fd5b505af115801561159b573d6000803e3d6000fd5b5050505061162d565b6013546001600160a01b039081169087160361162d5760135460a088015160405163d9a7951f60e01b8152600481018e90526001600160a01b038481166024830152604482019290925291169063d9a7951f90606401600060405180830381600087803b15801561161457600080fd5b505af1158015611628573d6000803e3d6000fd5b505050505b50505050505b6000868152600e60209081526040808320805489151560ff1991821617909155600f9092529091208054909116600117905581517fe16fea440372e1dff8f7e8cb79dcc6e6af20f2af3a278835a2049243b25d03ca90829088908890611698906119d0565b86602001518760400151886060015189608001518a60a001518d8c6101200151426040516117409c9b9a999897969594939291906001600160a01b039c8d168152602081019b909b5298151560408b015260608a019790975261ffff95861660808a01529390941660a088015260029190910b60c087015260ff1660e08601526101008501919091526101208401529092166101408201526101608101919091526101800190565b60405180910390a1505050505050565b61175861178b565b6001600160a01b038116611782576000604051631e4fbdf760e01b81526004016108a6919061242b565b6103f2816118a1565b6006546001600160a01b031633146106a7573360405163118cdaa760e01b81526004016108a6919061242b565b6117c06119ef565b6006805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516117fd919061242b565b60405180910390a1565b61180f6118f3565b6006805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586117f03390565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261189c908490611a14565b505050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6118fb610599565b156106a75760405163d93c066560e01b815260040160405180910390fd5b6119216123c3565b6119296123c3565b61193581868686611a6e565b9150505b9392505050565b608083015161194f9083611ab4565b608083015161189c9082611ab4565b608083015161196d9083611ab4565b608083015161189c9082611acb565b608083015161198b9083611ab4565b608083015161189c9082611af0565b60606119b06001600160a01b0383166014611b4f565b92915050565b600354600090611939906001600160a01b03168484611cc6565b8051600090829082036119e65750600092915050565b50506020015190565b6119f7610599565b6106a757604051638dfc202b60e01b815260040160405180910390fd5b6000611a296001600160a01b03841683611d59565b90508051600014158015611a4e575080806020019051810190611a4c91906127e3565b155b1561189c5782604051635274afe760e01b81526004016108a6919061242b565b611a766123c3565b611a868560800151610100611d67565b50508284526001600160a01b03821660208501526001600160e01b031981166040850152835b949350505050565b611ac18260038351611dbf565b61189c8282611ecc565b6001600160401b03811115611ae4576106338282611ee6565b61063382600083611dbf565b67ffffffffffffffff19811215611b0b576106338282611f1d565b6001600160401b03811315611b24576106338282611ee6565b60008112611b385761063382600083611dbf565b610633826001611b4a84600019612e62565b611dbf565b6060826000611b5f846002612be2565b611b6a906002612bcf565b6001600160401b03811115611b8157611b816128d3565b6040519080825280601f01601f191660200182016040528015611bab576020820181803683370190505b509050600360fc1b81600081518110611bc657611bc6612c23565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110611bf557611bf5612c23565b60200101906001600160f81b031916908160001a9053506000611c19856002612be2565b611c24906001612bcf565b90505b6001811115611c9c576f181899199a1a9b1b9c1cb0b131b232b360811b83600f1660108110611c5857611c58612c23565b1a60f81b828281518110611c6e57611c6e612c23565b60200101906001600160f81b031916908160001a90535060049290921c91611c9581612e89565b9050611c27565b508115611aac5760405163e22e27eb60e01b815260048101869052602481018590526044016108a6565b600454600090611cd7816001612bcf565b600455835160408086015160808701515191516000936320214ca360e11b93611d0f9386938493923092918a91600191602401612ea0565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091529050611d4f86838684611f4b565b9695505050505050565b6060611939838360006120a9565b611d6f6123f8565b611d7a602083612f08565b15611da257611d8a602083612f08565b611d95906020612f1c565b611d9f9083612bcf565b91505b506020828101829052604080518085526000815290920101905290565b6017816001600160401b031611611de957611de38360e0600585901b16831761213c565b50505050565b60ff816001600160401b031611611e2557611e0f836018611fe0600586901b161761213c565b50611de3836001600160401b0383166001612154565b61ffff816001600160401b031611611e6257611e4c836019611fe0600586901b161761213c565b50611de3836001600160401b0383166002612154565b63ffffffff816001600160401b031611611ea157611e8b83601a611fe0600586901b161761213c565b50611de3836001600160401b0383166004612154565b611eb683601b611fe0600586901b161761213c565b50611de3836001600160401b0383166008612154565b611ed46123f8565b6119398384600001515184855161216d565b611ef18260c261213c565b506106338282604051602001611f0991815260200190565b60405160208183030381529060405261224a565b611f288260c361213c565b5061063382611f3983600019612e62565b604051602001611f0991815260200190565b6040516bffffffffffffffffffffffff193060601b1660208201526034810184905260009060540160408051808303601f1901815282825280516020918201206000818152600590925291812080546001600160a01b0319166001600160a01b038a1617905590925082917fb5e6e01e79f91267dc17b4e6314d5d4d03593d2ceee0fbb452b750bd70ea5af99190a2600254604051630200057560e51b81526001600160a01b0390911690634000aea09061200e90889087908790600401612f2f565b6020604051808303816000875af115801561202d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061205191906127e3565b611aac5760405162461bcd60e51b815260206004820152602360248201527f756e61626c6520746f207472616e73666572416e6443616c6c20746f206f7261604482015262636c6560e81b60648201526084016108a6565b6060814710156120ce573060405163cd78605960e01b81526004016108a6919061242b565b600080856001600160a01b031684866040516120ea9190612f5f565b60006040518083038185875af1925050503d8060008114612127576040519150601f19603f3d011682016040523d82523d6000602084013e61212c565b606091505b5091509150611d4f868383612257565b6121446123f8565b61193983846000015151846122aa565b61215c6123f8565b611aac8485600001515185856122f8565b6121756123f8565b825182111561218357600080fd5b60208501516121928386612bcf565b11156121c5576121c5856121b5876020015187866121b09190612bcf565b61236c565b6121c0906002612be2565b612383565b6000808651805187602083010193508088870111156121e45787860182525b505050602084015b602084106122245780518252612203602083612bcf565b9150612210602082612bcf565b905061221d602085612f1c565b93506121ec565b51815160001960208690036101000a019081169019919091161790525083949350505050565b611ac18260028351611dbf565b60608261226c576122678261239a565b611939565b815115801561228357506001600160a01b0384163b155b156122a35783604051639996b31560e01b81526004016108a6919061242b565b5080611939565b6122b26123f8565b836020015183106122d2576122d284856020015160026121c09190612be2565b83518051602085830101848153508085036122ee576001810182525b5093949350505050565b6123006123f8565b602085015161230f8584612bcf565b111561232357612323856121b58685612bcf565b600060016123338461010061305f565b61233d9190612f1c565b90508551838682010185831982511617815250805184870111156123615783860181525b509495945050505050565b60008183111561237d5750816119b0565b50919050565b815161238f8383611d67565b50611de38382611ecc565b8051156123aa5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6040805160a0810182526000808252602082018190529181018290526060810191909152608081016123f36123f8565b905290565b604051806040016040528060608152602001600081525090565b60006020828403121561242457600080fd5b5035919050565b6001600160a01b0391909116815260200190565b80151581146103f257600080fd5b60006020828403121561245f57600080fd5b81356119398161243f565b60005b8381101561248557818101518382015260200161246d565b50506000910152565b600081518084526124a681602086016020860161246a565b601f01601f19169290920160200192915050565b60208152600082516101608060208501526124d961018085018361248e565b915060208501516124f0604086018261ffff169052565b50604085015161ffff81166060860152506060850151612515608086018260020b9052565b50608085015160ff811660a08601525060a085015160c085015260c085015160e085015260e0850151610100818187015280870151915050610120612564818701836001600160a01b03169052565b8601519050610140612580868201836001600160a01b03169052565b9095015162ffffff1693019290925250919050565b6001600160a01b03811681146103f257600080fd5b600080604083850312156125bd57600080fd5b82356125c881612595565b915060208301356125d881612595565b809150509250929050565b6000602082840312156125f557600080fd5b813561193981612595565b60006020828403121561261257600080fd5b81356001600160401b0381111561262857600080fd5b8201610160818503121561193957600080fd5b600080600080600060a0868803121561265357600080fd5b853561265e81612595565b9450602086013561266e81612595565b9350604086013561267e81612595565b94979396509394606081013594506080013592915050565b60006101608083526126aa8184018f61248e565b61ffff9d8e1660208501529b909c1660408301525060029890980b606089015260ff96909616608088015260a087019490945260c086019290925260e08501526001600160a01b039081166101008501521661012083015262ffffff166101409091015292915050565b60008060006060848603121561272957600080fd5b83359250602084013561273b8161243f565b929592945050506040919091013590565b600181811c9082168061276057607f821691505b60208210810361237d57634e487b7160e01b600052602260045260246000fd5b60006020828403121561279257600080fd5b5051919050565b6000602082840312156127ab57600080fd5b815161193981612595565b61ffff811681146103f257600080fd5b6000602082840312156127d857600080fd5b8135611939816127b6565b6000602082840312156127f557600080fd5b81516119398161243f565b6000808335601e1984360301811261281757600080fd5b8301803591506001600160401b0382111561283157600080fd5b60200191503681900382131561284657600080fd5b9250929050565b8060020b81146103f257600080fd5b60006020828403121561286e57600080fd5b81356119398161284d565b60ff811681146103f257600080fd5b60006020828403121561289a57600080fd5b813561193981612879565b62ffffff811681146103f257600080fd5b6000602082840312156128c857600080fd5b8135611939816128a5565b634e487b7160e01b600052604160045260246000fd5b601f82111561189c57600081815260208120601f850160051c810160208610156129105750805b601f850160051c820191505b8181101561292f5782815560010161291c565b505050505050565b6001600160401b0383111561294e5761294e6128d3565b6129628361295c835461274c565b836128e9565b6000601f841160018114612996576000851561297e5750838201355b600019600387901b1c1916600186901b1783556129f0565b600083815260209020601f19861690835b828110156129c757868501358255602094850194600190920191016129a7565b50868210156129e45760001960f88860031b161c19848701351681555b505060018560011b0183555b5050505050565b600081356119b0816127b6565b600081356119b08161284d565b600081356119b081612879565b600081356119b081612595565b80546001600160a01b0319166001600160a01b0392909216919091179055565b600081356119b0816128a5565b612a628283612800565b612a6d818385612937565b505060018101612a95612a82602085016129f7565b825461ffff191661ffff91909116178255565b612abe612aa4604085016129f7565b825463ffff0000191660109190911b63ffff000016178255565b612aef612acd60608501612a04565b825460209190911b66ffffff000000001666ffffff0000000019909116178255565b612b1c612afe60808501612a11565b82805460ff60381b191660389290921b60ff60381b16919091179055565b5060a0820135600282015560c0820135600382015560e08201356004820155612b54612b4b6101008401612a1e565b60058301612a2b565b60068101612b6e612b686101208501612a1e565b82612a2b565b61189c612b7e6101408501612a4b565b82805462ffffff60a01b191660a09290921b62ffffff60a01b16919091179055565b634e487b7160e01b600052601160045260246000fd5b600060018201612bc857612bc8612ba0565b5060010190565b808201808211156119b0576119b0612ba0565b80820281158282048414176119b0576119b0612ba0565b634e487b7160e01b600052601260045260246000fd5b600082612c1e57612c1e612bf9565b500490565b634e487b7160e01b600052603260045260246000fd5b600081518084526020808501945080840160005b8381101561236157815187529582019590820190600101612c4d565b600081518084526020808501808196508360051b810191508286016000805b86811015612cf7578385038a52825180518087529087019087870190845b81811015612ce2578351805161ffff1684528a81015160ff168b85015260409081015160020b9084015292890192606090920191600101612ca6565b50509a87019a95505091850191600101612c88565b509298975050505050505050565b600060c0808301818452808a5180835260e092508286019150828160051b8701016020808e0160005b84811015612e105789840360df190186528151805185528381015161ffff90811685870152604080830151909116908601526060808201519086015260808082015160ff169086015260a08082015160020b908601528881015162ffffff16898601528781015161016089870181905290612dab82880182612c39565b9150506101008083015187830382890152612dc68382612c39565b9250505061012080830151612ddf8289018260ff169052565b50506101408083015192508682038188015250612dfc8183612c69565b978501979550505090820190600101612d2e565b505087018c9052604087018b90526001600160a01b038a1660608801529450612e399350505050565b6001600160a01b03841660808301526001600160a01b03831660a0830152979650505050505050565b8181036000831280158383131683831282161715612e8257612e82612ba0565b5092915050565b600081612e9857612e98612ba0565b506000190190565b6001600160a01b0389811682526020820189905260408201889052861660608201526001600160e01b03198516608082015260a0810184905260c0810183905261010060e08201819052600090612ef98382018561248e565b9b9a5050505050505050505050565b600082612f1757612f17612bf9565b500690565b818103818111156119b0576119b0612ba0565b60018060a01b0384168152826020820152606060408201526000612f56606083018461248e565b95945050505050565b60008251612f7181846020870161246a565b9190910192915050565b600181815b80851115612fb6578160001904821115612f9c57612f9c612ba0565b80851615612fa957918102915b93841c9390800290612f80565b509250929050565b600082612fcd575060016119b0565b81612fda575060006119b0565b8160018114612ff05760028114612ffa57613016565b60019150506119b0565b60ff84111561300b5761300b612ba0565b50506001821b6119b0565b5060208310610133831016604e8410600b8410161715613039575081810a6119b0565b6130438383612f7b565b806000190482111561305757613057612ba0565b029392505050565b60006119398383612fbe56fea264697066735822122085cc9998b447cff0a1e45985f1c0dd54a17800dd8d93fd8b7e184ddef8d1ff9c64736f6c63430008140033

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

000000000000000000000000149459ad88d23d2ce0f4f1371203db2122331d94000000000000000000000000b7dec0966366ecc5a1906f83caa12cca2edb0a8c00000000000000000000000076923cdde21928ddbec4b8bfdc8143bb6d0841a864316539393636633462386434383738386335633763333962356338383532640000000000000000000000000000000000000000000000000de0b6b3a7640000

-----Decoded View---------------
Arg [0] : _link (address): 0x149459AD88d23d2ce0F4F1371203Db2122331d94
Arg [1] : _oracle (address): 0xB7dec0966366ecC5A1906F83caA12CCA2EDb0a8c
Arg [2] : _sportsAMM (address): 0x76923cDDE21928ddbeC4B8BFDC8143BB6d0841a8
Arg [3] : _jobSpecId (bytes32): 0x6431653939363663346238643438373838633563376333396235633838353264
Arg [4] : _paymentAmount (uint256): 1000000000000000000

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000149459ad88d23d2ce0f4f1371203db2122331d94
Arg [1] : 000000000000000000000000b7dec0966366ecc5a1906f83caa12cca2edb0a8c
Arg [2] : 00000000000000000000000076923cdde21928ddbec4b8bfdc8143bb6d0841a8
Arg [3] : 6431653939363663346238643438373838633563376333396235633838353264
Arg [4] : 0000000000000000000000000000000000000000000000000de0b6b3a7640000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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