Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
ParlayAMMLiquidityPool
Compiler Version
v0.8.4+commit.c7e474f2
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "../../../utils/proxy/solidity-0.8.0/ProxyReentrancyGuard.sol";
import "../../../utils/proxy/solidity-0.8.0/ProxyOwned.sol";
import "@openzeppelin/contracts-4.4.1/proxy/Clones.sol";
import "../../../interfaces/ISportsAMM.sol";
import "../../../interfaces/IParlayMarketsAMM.sol";
import "../../../interfaces/ISportPositionalMarket.sol";
import "../../../interfaces/IStakingThales.sol";
import "./ParlayAMMLiquidityPoolRound.sol";
contract ParlayAMMLiquidityPool is Initializable, ProxyOwned, PausableUpgradeable, ProxyReentrancyGuard {
/* ========== LIBRARIES ========== */
using SafeERC20Upgradeable for IERC20Upgradeable;
struct InitParams {
address _owner;
address _parlayAMM;
IERC20Upgradeable _sUSD;
uint _roundLength;
uint _maxAllowedDeposit;
uint _minDepositAmount;
uint _maxAllowedUsers;
bool _needsTransformingCollateral;
}
/* ========== CONSTANTS ========== */
uint private constant HUNDRED = 1e20;
uint private constant ONE = 1e18;
uint private constant ONE_PERCENT = 1e16;
/* ========== STATE VARIABLES ========== */
IParlayMarketsAMM public parlayAMM;
IERC20Upgradeable public sUSD;
bool public started;
uint public round;
uint public roundLength;
//actually second round, as first one is default for mixed round and never closes
uint public firstRoundStartTime;
mapping(uint => address) public roundPools;
mapping(uint => address[]) public usersPerRound;
mapping(uint => mapping(address => bool)) public userInRound;
mapping(uint => mapping(address => uint)) public balancesPerRound;
mapping(uint => uint) public allocationPerRound;
mapping(address => bool) public withdrawalRequested;
mapping(uint => address[]) public tradingMarketsPerRound;
mapping(uint => mapping(address => bool)) public isTradingMarketInARound;
mapping(uint => uint) public profitAndLossPerRound;
mapping(uint => uint) public cumulativeProfitAndLoss;
uint public maxAllowedDeposit;
uint public minDepositAmount;
uint public maxAllowedUsers;
uint public usersCurrentlyInPool;
address public defaultLiquidityProvider;
IStakingThales public stakingThales;
uint public stakedThalesMultiplier;
address public poolRoundMastercopy;
mapping(address => bool) public whitelistedDeposits;
uint public totalDeposited;
bool public onlyWhitelistedStakersAllowed;
mapping(address => bool) public whitelistedStakers;
bool public needsTransformingCollateral;
mapping(uint => mapping(address => bool)) public marketAlreadyExercisedInRound;
bool public roundClosingPrepared;
uint public usersProcessedInRound;
mapping(address => uint) public withdrawalShare;
mapping(address => uint) public parlayMarketRound;
uint public utilizationRate;
address public safeBox;
uint public safeBoxImpact;
/* ========== CONSTRUCTOR ========== */
// check git
function initialize(InitParams calldata params) external initializer {
setOwner(params._owner);
initNonReentrant();
parlayAMM = IParlayMarketsAMM(params._parlayAMM);
sUSD = params._sUSD;
roundLength = params._roundLength;
maxAllowedDeposit = params._maxAllowedDeposit;
minDepositAmount = params._minDepositAmount;
maxAllowedUsers = params._maxAllowedUsers;
needsTransformingCollateral = params._needsTransformingCollateral;
sUSD.approve(params._parlayAMM, type(uint256).max);
round = 1;
}
/// @notice Start pool and begin round #1
function start() external onlyOwner {
require(!started, "Liquidity pool has already started");
require(allocationPerRound[2] > 0, "can not start with 0 deposits");
firstRoundStartTime = block.timestamp;
round = 2;
address roundPool = _getOrCreateRoundPool(2);
ParlayAMMLiquidityPoolRound(roundPool).updateRoundTimes(firstRoundStartTime, getRoundEndTime(2));
started = true;
emit PoolStarted();
}
/// @notice Deposit funds from user into pool for the next round
/// @param amount Value to be deposited
function deposit(uint amount) external canDeposit(amount) nonReentrant whenNotPaused roundClosingNotPrepared {
uint nextRound = round + 1;
address roundPool = _getOrCreateRoundPool(nextRound);
sUSD.safeTransferFrom(msg.sender, roundPool, amount);
require(msg.sender != defaultLiquidityProvider, "Can't deposit directly as default liquidity provider");
// new user enters the pool
if (balancesPerRound[round][msg.sender] == 0 && balancesPerRound[nextRound][msg.sender] == 0) {
require(usersCurrentlyInPool < maxAllowedUsers, "Max amount of users reached");
usersPerRound[nextRound].push(msg.sender);
usersCurrentlyInPool = usersCurrentlyInPool + 1;
}
balancesPerRound[nextRound][msg.sender] += amount;
allocationPerRound[nextRound] += amount;
totalDeposited += amount;
if (address(stakingThales) != address(0)) {
stakingThales.updateVolume(msg.sender, amount);
}
emit Deposited(msg.sender, amount, round);
}
/// @notice get sUSD to mint for buy and store market as trading in the round
/// @param market to trade
/// @param amountToMint amount to get for mint
function commitTrade(address market, uint amountToMint)
external
nonReentrant
whenNotPaused
onlyAMM
roundClosingNotPrepared
{
require(started, "Pool has not started");
require(amountToMint > 0, "Can't commit a zero trade");
amountToMint = _transformCollateral(amountToMint);
// add 1e-6 due to rounding issue, will be sent back to AMM at the end
amountToMint = needsTransformingCollateral ? amountToMint + 1 : amountToMint;
uint marketRound = getMarketRound(market);
parlayMarketRound[market] = marketRound;
address liquidityPoolRound = _getOrCreateRoundPool(marketRound);
if (marketRound == round) {
sUSD.safeTransferFrom(liquidityPoolRound, address(parlayAMM), amountToMint);
require(
sUSD.balanceOf(liquidityPoolRound) >=
(allocationPerRound[round] - ((allocationPerRound[round] * utilizationRate) / ONE)),
"Amount exceeds available utilization for round"
);
} else if (marketRound > round) {
uint poolBalance = sUSD.balanceOf(liquidityPoolRound);
if (poolBalance >= amountToMint) {
sUSD.safeTransferFrom(liquidityPoolRound, address(parlayAMM), amountToMint);
} else {
uint differenceToLPAsDefault = amountToMint - poolBalance;
_depositAsDefault(differenceToLPAsDefault, liquidityPoolRound, marketRound);
sUSD.safeTransferFrom(liquidityPoolRound, address(parlayAMM), amountToMint);
}
} else {
require(marketRound == 1, "InvalidRound");
_provideAsDefault(amountToMint);
}
tradingMarketsPerRound[marketRound].push(market);
isTradingMarketInARound[marketRound][market] = true;
}
function transferToPool(address _market, uint _amount) external whenNotPaused roundClosingNotPrepared onlyAMM {
uint marketRound = getMarketRound(_market);
address liquidityPoolRound = marketRound <= 1 ? defaultLiquidityProvider : _getOrCreateRoundPool(marketRound);
sUSD.safeTransferFrom(address(parlayAMM), liquidityPoolRound, _amount);
if (isTradingMarketInARound[marketRound][_market]) {
marketAlreadyExercisedInRound[marketRound][_market] = true;
}
}
/// @notice Create a round pool by market maturity date if it doesnt already exist
/// @param market to use
/// @return roundPool the pool for the passed market
function getOrCreateMarketPool(address market)
external
onlyAMM
nonReentrant
whenNotPaused
roundClosingNotPrepared
returns (address roundPool)
{
uint marketRound = getMarketRound(market);
roundPool = _getOrCreateRoundPool(marketRound);
}
/// @notice request withdrawal from the LP
function withdrawalRequest() external nonReentrant canWithdraw whenNotPaused roundClosingNotPrepared {
if (totalDeposited > balancesPerRound[round][msg.sender]) {
totalDeposited -= balancesPerRound[round][msg.sender];
} else {
totalDeposited = 0;
}
usersCurrentlyInPool = usersCurrentlyInPool - 1;
withdrawalRequested[msg.sender] = true;
emit WithdrawalRequested(msg.sender);
}
/// @notice request partial withdrawal from the LP.
/// @param share the percentage the user is wihdrawing from his total deposit
function partialWithdrawalRequest(uint share) external nonReentrant canWithdraw whenNotPaused roundClosingNotPrepared {
require(share >= ONE_PERCENT * 10 && share <= ONE_PERCENT * 90, "Share has to be between 10% and 90%");
uint toWithdraw = (balancesPerRound[round][msg.sender] * share) / ONE;
if (totalDeposited > toWithdraw) {
totalDeposited -= toWithdraw;
} else {
totalDeposited = 0;
}
withdrawalRequested[msg.sender] = true;
withdrawalShare[msg.sender] = share;
emit WithdrawalRequested(msg.sender);
}
/// @notice Prepare round closing
/// excercise options of trading markets and ensure there are no markets left unresolved
function prepareRoundClosing() external nonReentrant whenNotPaused roundClosingNotPrepared {
require(canCloseCurrentRound(), "Can't close current round");
// excercise market options
exerciseMarketsReadyToExercised();
address roundPool = roundPools[round];
// final balance is the final amount of sUSD in the round pool
uint currentBalance = sUSD.balanceOf(roundPool);
// send profit reserved for SafeBox if positive round
if (currentBalance > allocationPerRound[round]) {
uint safeBoxAmount = ((currentBalance - allocationPerRound[round]) * safeBoxImpact) / ONE;
sUSD.safeTransferFrom(roundPool, safeBox, safeBoxAmount);
currentBalance = currentBalance - safeBoxAmount;
emit SafeBoxSharePaid(safeBoxImpact, safeBoxAmount);
}
// calculate PnL
// if no allocation for current round
if (allocationPerRound[round] == 0) {
profitAndLossPerRound[round] = 1;
} else {
profitAndLossPerRound[round] = (currentBalance * ONE) / allocationPerRound[round];
}
roundClosingPrepared = true;
emit RoundClosingPrepared(round);
}
/// @notice Prepare round closing
/// excercise options of trading markets and ensure there are no markets left unresolved
function processRoundClosingBatch(uint batchSize) external nonReentrant whenNotPaused {
require(roundClosingPrepared, "Round closing not prepared");
require(usersProcessedInRound < usersPerRound[round].length, "All users already processed");
require(batchSize > 0, "batchSize has to be greater than 0");
address roundPool = roundPools[round];
uint endCursor = usersProcessedInRound + batchSize;
if (endCursor > usersPerRound[round].length) {
endCursor = usersPerRound[round].length;
}
for (uint i = usersProcessedInRound; i < endCursor; i++) {
address user = usersPerRound[round][i];
uint balanceAfterCurRound = (balancesPerRound[round][user] * profitAndLossPerRound[round]) / ONE;
if (!withdrawalRequested[user] && (profitAndLossPerRound[round] > 0)) {
balancesPerRound[round + 1][user] = balancesPerRound[round + 1][user] + balanceAfterCurRound;
usersPerRound[round + 1].push(user);
if (address(stakingThales) != address(0)) {
stakingThales.updateVolume(user, balanceAfterCurRound);
}
} else {
if (withdrawalShare[user] > 0) {
uint amountToClaim = (balanceAfterCurRound * withdrawalShare[user]) / ONE;
sUSD.safeTransferFrom(roundPool, user, amountToClaim);
emit Claimed(user, amountToClaim);
withdrawalRequested[user] = false;
withdrawalShare[user] = 0;
usersPerRound[round + 1].push(user);
balancesPerRound[round + 1][user] = balanceAfterCurRound - amountToClaim;
} else {
balancesPerRound[round + 1][user] = 0;
sUSD.safeTransferFrom(roundPool, user, balanceAfterCurRound);
withdrawalRequested[user] = false;
emit Claimed(user, balanceAfterCurRound);
}
}
usersProcessedInRound = usersProcessedInRound + 1;
}
emit RoundClosingBatchProcessed(round, batchSize);
}
/// @notice Close current round and begin next round,
/// calculate profit and loss and process withdrawals
function closeRound() external nonReentrant whenNotPaused {
require(roundClosingPrepared, "Round closing not prepared");
require(usersProcessedInRound == usersPerRound[round].length, "Not all users processed yet");
// set for next round to false
roundClosingPrepared = false;
address roundPool = roundPools[round];
//always claim for defaultLiquidityProvider
if (balancesPerRound[round][defaultLiquidityProvider] > 0) {
uint balanceAfterCurRound = (balancesPerRound[round][defaultLiquidityProvider] * profitAndLossPerRound[round]) /
ONE;
sUSD.safeTransferFrom(roundPool, defaultLiquidityProvider, balanceAfterCurRound);
emit Claimed(defaultLiquidityProvider, balanceAfterCurRound);
}
if (round == 2) {
cumulativeProfitAndLoss[round] = profitAndLossPerRound[round];
} else {
cumulativeProfitAndLoss[round] = (cumulativeProfitAndLoss[round - 1] * profitAndLossPerRound[round]) / ONE;
}
// start next round
++round;
//add all carried over sUSD
allocationPerRound[round] += sUSD.balanceOf(roundPool);
totalDeposited = allocationPerRound[round] - balancesPerRound[round][defaultLiquidityProvider];
address roundPoolNewRound = _getOrCreateRoundPool(round);
sUSD.safeTransferFrom(roundPool, roundPoolNewRound, sUSD.balanceOf(roundPool));
usersProcessedInRound = 0;
emit RoundClosed(round - 1, profitAndLossPerRound[round - 1]);
}
/// @notice Iterate all markets in the current round and exercise those ready to be exercised
function exerciseMarketsReadyToExercised() public roundClosingNotPrepared {
ParlayAMMLiquidityPoolRound poolRound = ParlayAMMLiquidityPoolRound(roundPools[round]);
ParlayMarket market;
address marketAddress;
for (uint i = 0; i < tradingMarketsPerRound[round].length; i++) {
marketAddress = tradingMarketsPerRound[round][i];
if (!marketAlreadyExercisedInRound[round][marketAddress]) {
market = ParlayMarket(marketAddress);
(bool isExercisable, ) = market.isParlayExercisable();
if (isExercisable && !market.isUserTheWinner()) {
parlayAMM.exerciseParlay(marketAddress);
}
if (market.isUserTheWinner() || market.resolved()) {
marketAlreadyExercisedInRound[round][marketAddress] = true;
}
}
}
}
/// @notice Exercises markets in a round
/// @param batchSize number of markets to be processed
function exerciseMarketsReadyToExercisedBatch(uint batchSize)
external
nonReentrant
whenNotPaused
roundClosingNotPrepared
{
require(batchSize > 0, "batchSize has to be greater than 0");
ParlayAMMLiquidityPoolRound poolRound = ParlayAMMLiquidityPoolRound(roundPools[round]);
uint count = 0;
ParlayMarket market;
for (uint i = 0; i < tradingMarketsPerRound[round].length; i++) {
if (count == batchSize) break;
address marketAddress = tradingMarketsPerRound[round][i];
if (!marketAlreadyExercisedInRound[round][marketAddress]) {
market = ParlayMarket(marketAddress);
(bool isExercisable, ) = market.isParlayExercisable();
if (isExercisable && !market.isUserTheWinner()) {
parlayAMM.exerciseParlay(marketAddress);
}
if (market.isUserTheWinner() || market.resolved()) {
marketAlreadyExercisedInRound[round][marketAddress] = true;
count += 1;
}
}
}
}
/// @notice retrieve surplus funds
function retrieveLeftoverRoundFunds(uint[] calldata rounds) external nonReentrant whenNotPaused onlyOwner {
for (uint i = 0; i < rounds.length; i++) {
uint iteratedRound = rounds[i];
require(iteratedRound > 1, "Can't pull from default rounds");
require(iteratedRound < round, "Can't pull from current or future rounds");
address roundPool = roundPools[iteratedRound];
uint currentBalance = sUSD.balanceOf(roundPool);
sUSD.safeTransferFrom(roundPool, msg.sender, currentBalance);
emit LeftoverFundsPulled(round);
}
}
/* ========== VIEWS ========== */
/// @notice whether the user is currently LPing
/// @param user to check
/// @return isUserInLP whether the user is currently LPing
function isUserLPing(address user) external view returns (bool isUserInLP) {
isUserInLP =
(balancesPerRound[round][user] > 0 || balancesPerRound[round + 1][user] > 0) &&
(!withdrawalRequested[user] || withdrawalShare[user] > 0);
}
/// @notice Return the maximum amount the user can deposit now
/// @param user address to check
/// @return maxDepositForUser the maximum amount the user can deposit in total including already deposited
/// @return availableToDepositForUser the maximum amount the user can deposit now
/// @return stakedThalesForUser how much THALES the user has staked
function getMaxAvailableDepositForUser(address user)
external
view
returns (
uint maxDepositForUser,
uint availableToDepositForUser,
uint stakedThalesForUser
)
{
uint nextRound = round + 1;
stakedThalesForUser = stakingThales.stakedBalanceOf(user);
maxDepositForUser = _transformCollateral((stakedThalesForUser * stakedThalesMultiplier) / ONE);
availableToDepositForUser = maxDepositForUser > (balancesPerRound[round][user] + balancesPerRound[nextRound][user])
? (maxDepositForUser - balancesPerRound[round][user] - balancesPerRound[nextRound][user])
: 0;
}
/// @notice get the pool address for the market
/// @param market to check
/// @return roundPool the pool address for the market
function getMarketPool(address market) external view returns (address roundPool) {
roundPool = roundPools[getMarketRound(market)];
}
/// @notice Checks if all conditions are met to close the round
/// @return bool
function canCloseCurrentRound() public view returns (bool) {
if (!started || block.timestamp < getRoundEndTime(round)) {
return false;
}
ParlayMarket market;
for (uint i = 0; i < tradingMarketsPerRound[round].length; i++) {
address marketAddress = tradingMarketsPerRound[round][i];
if (!marketAlreadyExercisedInRound[round][marketAddress]) {
market = ParlayMarket(marketAddress);
if (!market.areAllPositionsResolved()) {
return false;
}
}
}
return true;
}
/// @notice Iterate all markets in the current round and return true if at least one can be exercised
function hasMarketsReadyToBeExercised() public view returns (bool) {
ParlayMarket market;
address marketAddress;
for (uint i = 0; i < tradingMarketsPerRound[round].length; i++) {
marketAddress = tradingMarketsPerRound[round][i];
if (!marketAlreadyExercisedInRound[round][marketAddress]) {
market = ParlayMarket(marketAddress);
(bool isExercisable, ) = market.isParlayExercisable();
if (isExercisable && !market.isUserTheWinner()) {
return true;
}
}
}
return false;
}
/// @notice Return multiplied PnLs between rounds
/// @param roundA Round number from
/// @param roundB Round number to
/// @return uint
function cumulativePnLBetweenRounds(uint roundA, uint roundB) public view returns (uint) {
return (cumulativeProfitAndLoss[roundB] * profitAndLossPerRound[roundA]) / cumulativeProfitAndLoss[roundA];
}
/// @notice Return the start time of the passed round
/// @param _round number
/// @return uint the start time of the given round
function getRoundStartTime(uint _round) public view returns (uint) {
return firstRoundStartTime + (_round - 2) * roundLength;
}
/// @notice Return the end time of the passed round
/// @param _round number
/// @return uint the end time of the given round
function getRoundEndTime(uint _round) public view returns (uint) {
return firstRoundStartTime + (_round - 1) * roundLength;
}
/// @notice Return the round to which a market belongs to
/// @param market to get the round for
/// @return _round the min round which the market belongs to
function getMarketRound(address market) public view returns (uint _round) {
_round = parlayMarketRound[market];
if (_round == 0) {
ParlayMarket parlayMarket = ParlayMarket(market);
address sportMarket;
for (uint i = 0; i < parlayMarket.numOfSportMarkets(); i++) {
(sportMarket, , , , , , , ) = parlayMarket.sportMarket(i);
ISportPositionalMarket marketContract = ISportPositionalMarket(sportMarket);
(uint maturity, ) = marketContract.times();
if (maturity > firstRoundStartTime) {
if (i == 0) {
_round = (maturity - firstRoundStartTime) / roundLength + 2;
} else {
if (((maturity - firstRoundStartTime) / roundLength + 2) != _round) {
_round = 1;
break;
}
}
} else {
_round = 1;
}
}
}
}
/// @notice Return the count of users in current round
/// @return _the count of users in current round
function getUsersCountInCurrentRound() external view returns (uint) {
return usersPerRound[round].length;
}
function getTradingMarketsPerRound(uint _round) external view returns (uint numOfMarkets) {
numOfMarkets = tradingMarketsPerRound[_round].length;
}
/* ========== INTERNAL FUNCTIONS ========== */
function _transformCollateral(uint value) internal view returns (uint) {
if (needsTransformingCollateral) {
return value / 1e12;
} else {
return value;
}
}
function _reverseTransformCollateral(uint value) internal view returns (uint) {
if (needsTransformingCollateral) {
return value * 1e12;
} else {
return value;
}
}
function _depositAsDefault(
uint amount,
address roundPool,
uint _round
) internal {
require(defaultLiquidityProvider != address(0), "default liquidity provider not set");
sUSD.safeTransferFrom(defaultLiquidityProvider, roundPool, amount);
balancesPerRound[_round][defaultLiquidityProvider] += amount;
allocationPerRound[_round] += amount;
emit Deposited(defaultLiquidityProvider, amount, _round);
}
function _provideAsDefault(uint amount) internal {
require(defaultLiquidityProvider != address(0), "default liquidity provider not set");
sUSD.safeTransferFrom(defaultLiquidityProvider, address(parlayAMM), amount);
balancesPerRound[1][defaultLiquidityProvider] += amount;
allocationPerRound[1] += amount;
emit Deposited(defaultLiquidityProvider, amount, 1);
}
function _getOrCreateRoundPool(uint _round) internal returns (address roundPool) {
roundPool = roundPools[_round];
if (roundPool == address(0)) {
if (_round == 1) {
roundPools[_round] = defaultLiquidityProvider;
roundPool = defaultLiquidityProvider;
} else {
require(poolRoundMastercopy != address(0), "Round pool mastercopy not set");
ParlayAMMLiquidityPoolRound newRoundPool = ParlayAMMLiquidityPoolRound(Clones.clone(poolRoundMastercopy));
newRoundPool.initialize(address(this), sUSD, _round, getRoundEndTime(_round - 1), getRoundEndTime(_round));
roundPool = address(newRoundPool);
roundPools[_round] = roundPool;
emit RoundPoolCreated(_round, roundPool);
}
}
}
/* ========== SETTERS ========== */
function setPaused(bool _setPausing) external onlyOwner {
_setPausing ? _pause() : _unpause();
}
/// @notice Set _poolRoundMastercopy
/// @param _poolRoundMastercopy to clone round pools from
function setPoolRoundMastercopy(address _poolRoundMastercopy) external onlyOwner {
require(_poolRoundMastercopy != address(0), "Can not set a zero address!");
poolRoundMastercopy = _poolRoundMastercopy;
emit PoolRoundMastercopyChanged(poolRoundMastercopy);
}
/// @notice Set IStakingThales contract
/// @param _stakingThales IStakingThales address
function setStakingThales(IStakingThales _stakingThales) external onlyOwner {
require(address(_stakingThales) != address(0), "Can not set a zero address!");
stakingThales = _stakingThales;
emit StakingThalesChanged(address(_stakingThales));
}
/// @notice Set max allowed deposit
/// @param _maxAllowedDeposit Deposit value
function setMaxAllowedDeposit(uint _maxAllowedDeposit) external onlyOwner {
maxAllowedDeposit = _maxAllowedDeposit;
emit MaxAllowedDepositChanged(_maxAllowedDeposit);
}
/// @notice Set min allowed deposit
/// @param _minDepositAmount Deposit value
function setMinAllowedDeposit(uint _minDepositAmount) external onlyOwner {
minDepositAmount = _minDepositAmount;
emit MinAllowedDepositChanged(_minDepositAmount);
}
/// @notice Set _maxAllowedUsers
/// @param _maxAllowedUsers Deposit value
function setMaxAllowedUsers(uint _maxAllowedUsers) external onlyOwner {
maxAllowedUsers = _maxAllowedUsers;
emit MaxAllowedUsersChanged(_maxAllowedUsers);
}
/// @notice Set ThalesAMM contract
/// @param _parlayAMM ThalesAMM address
function setParlayAmm(IParlayMarketsAMM _parlayAMM) external onlyOwner {
require(address(_parlayAMM) != address(0), "Can not set a zero address!");
parlayAMM = _parlayAMM;
sUSD.approve(address(parlayAMM), type(uint256).max);
emit SportAMMChanged(address(_parlayAMM));
}
/// @notice Set defaultLiquidityProvider wallet
/// @param _defaultLiquidityProvider default liquidity provider
function setDefaultLiquidityProvider(address _defaultLiquidityProvider) external onlyOwner {
require(_defaultLiquidityProvider != address(0), "Can not set a zero address!");
defaultLiquidityProvider = _defaultLiquidityProvider;
emit DefaultLiquidityProviderChanged(_defaultLiquidityProvider);
}
/// @notice Set length of rounds
/// @param _roundLength Length of a round in miliseconds
function setRoundLength(uint _roundLength) external onlyOwner {
require(!started, "Can't change round length after start");
roundLength = _roundLength;
emit RoundLengthChanged(_roundLength);
}
/// @notice set utilization rate parameter
/// @param _utilizationRate value as percentage
function setUtilizationRate(uint _utilizationRate) external onlyOwner {
utilizationRate = _utilizationRate;
emit UtilizationRateChanged(_utilizationRate);
}
/// @notice set SafeBox params
/// @param _safeBox where to send a profit reserved for protocol from each round
/// @param _safeBoxImpact how much is the SafeBox percentage
function setSafeBoxParams(address _safeBox, uint _safeBoxImpact) external onlyOwner {
safeBox = _safeBox;
safeBoxImpact = _safeBoxImpact;
emit SetSafeBoxParams(_safeBox, _safeBoxImpact);
}
/* ========== MODIFIERS ========== */
modifier canDeposit(uint amount) {
require(!withdrawalRequested[msg.sender], "Withdrawal is requested, cannot deposit");
require(totalDeposited + amount <= maxAllowedDeposit, "Deposit amount exceeds AMM LP cap");
if (balancesPerRound[round][msg.sender] == 0 && balancesPerRound[round + 1][msg.sender] == 0) {
require(amount >= minDepositAmount, "Amount less than minDepositAmount");
}
_;
}
modifier canWithdraw() {
require(started, "Pool has not started");
require(!withdrawalRequested[msg.sender], "Withdrawal already requested");
require(balancesPerRound[round][msg.sender] > 0, "Nothing to withdraw");
require(balancesPerRound[round + 1][msg.sender] == 0, "Can't withdraw as you already deposited for next round");
_;
}
modifier onlyAMM() {
require(msg.sender == address(parlayAMM), "only the AMM may perform these methods");
_;
}
modifier roundClosingNotPrepared() {
require(!roundClosingPrepared, "Not allowed during roundClosingPrepared");
_;
}
/* ========== EVENTS ========== */
event PoolStarted();
event Deposited(address user, uint amount, uint round);
event WithdrawalRequested(address user);
event RoundClosed(uint round, uint roundPnL);
event Claimed(address user, uint amount);
event RoundPoolCreated(uint _round, address roundPool);
event PoolRoundMastercopyChanged(address newMastercopy);
event StakedThalesMultiplierChanged(uint _stakedThalesMultiplier);
event StakingThalesChanged(address stakingThales);
event MaxAllowedDepositChanged(uint maxAllowedDeposit);
event MinAllowedDepositChanged(uint minAllowedDeposit);
event MaxAllowedUsersChanged(uint MaxAllowedUsersChanged);
event SportAMMChanged(address sportAMM);
event DefaultLiquidityProviderChanged(address newProvider);
event AddedIntoWhitelist(address _whitelistAddress, bool _flag);
event AddedIntoWhitelistStaker(address _whitelistAddress, bool _flag);
event RoundLengthChanged(uint roundLength);
event RoundClosingPrepared(uint round);
event RoundClosingBatchProcessed(uint round, uint batchSize);
event UtilizationRateChanged(uint utilizationRate);
event SetSafeBoxParams(address safeBox, uint safeBoxImpact);
event SafeBoxSharePaid(uint safeBoxShare, uint safeBoxAmount);
event LeftoverFundsPulled(uint round);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
using AddressUpgradeable for address;
function safeTransfer(
IERC20Upgradeable token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20Upgradeable token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20Upgradeable token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20Upgradeable token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20Upgradeable token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @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(IERC20Upgradeable 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, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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.
*
* By default, the owner account will be the one that deploys the contract. 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 OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Context_init_unchained();
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing 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 {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_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);
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/utils/Initializable.sol)
pragma solidity ^0.8.0;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
* initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() initializer {}
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
// If the contract is initializing we ignore whether _initialized is set in order to support multiple
// inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
// contract may have been reentered.
require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} modifier, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
function _isConstructor() private view returns (bool) {
return !AddressUpgradeable.isContract(address(this));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Context_init_unchained();
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier
* available, which can be aplied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*/
contract ProxyReentrancyGuard {
/// @dev counter to allow mutex lock with only one SSTORE operation
uint256 private _guardCounter;
bool private _initialized;
function initNonReentrant() public {
require(!_initialized, "Already initialized");
_initialized = true;
_guardCounter = 1;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_guardCounter += 1;
uint256 localCounter = _guardCounter;
_;
require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Clone of syntetix contract without constructor
contract ProxyOwned {
address public owner;
address public nominatedOwner;
bool private _initialized;
bool private _transferredAtInit;
function setOwner(address _owner) public {
require(_owner != address(0), "Owner address cannot be 0");
require(!_initialized, "Already initialized, use nominateNewOwner");
_initialized = true;
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
function transferOwnershipAtInit(address proxyAddress) external onlyOwner {
require(proxyAddress != address(0), "Invalid address");
require(!_transferredAtInit, "Already transferred");
owner = proxyAddress;
_transferredAtInit = true;
emit OwnerChanged(owner, proxyAddress);
}
modifier onlyOwner {
_onlyOwner();
_;
}
function _onlyOwner() private view {
require(msg.sender == owner, "Only the contract owner may perform this action");
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/Clones.sol)
pragma solidity ^0.8.0;
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*
* _Available since v3.4._
*/
library Clones {
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, implementation))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create(0, ptr, 0x37)
}
require(instance != address(0), "ERC1167: create failed");
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, implementation))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
instance := create2(0, ptr, 0x37, salt)
}
require(instance != address(0), "ERC1167: create2 failed");
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(ptr, 0x14), shl(0x60, implementation))
mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
mstore(add(ptr, 0x38), shl(0x60, deployer))
mstore(add(ptr, 0x4c), salt)
mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
predicted := keccak256(add(ptr, 0x37), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(address implementation, bytes32 salt)
internal
view
returns (address predicted)
{
return predictDeterministicAddress(implementation, salt, address(this));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../interfaces/ISportAMMRiskManager.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
interface ISportsAMM {
/* ========== VIEWS / VARIABLES ========== */
enum Position {
Home,
Away,
Draw
}
struct SellRequirements {
address user;
address market;
Position position;
uint amount;
uint expectedPayout;
uint additionalSlippage;
}
function theRundownConsumer() external view returns (address);
function riskManager() external view returns (ISportAMMRiskManager riskManager);
function getMarketDefaultOdds(address _market, bool isSell) external view returns (uint[] memory);
function isMarketInAMMTrading(address _market) external view returns (bool);
function isMarketForSportOnePositional(uint _tag) external view returns (bool);
function availableToBuyFromAMM(address market, Position position) external view returns (uint _available);
function parlayAMM() external view returns (address);
function minSupportedOdds() external view returns (uint);
function maxSupportedOdds() external view returns (uint);
function minSupportedOddsPerSport(uint) external view returns (uint);
function min_spread() external view returns (uint);
function max_spread() external view returns (uint);
function minimalTimeLeftToMaturity() external view returns (uint);
function getSpentOnGame(address market) external view returns (uint);
function safeBoxImpact() external view returns (uint);
function manager() external view returns (address);
function getLiquidityPool() external view returns (address);
function sUSD() external view returns (IERC20Upgradeable);
function buyFromAMM(
address market,
Position position,
uint amount,
uint expectedPayout,
uint additionalSlippage
) external;
function buyFromAmmQuote(
address market,
Position position,
uint amount
) external view returns (uint);
function buyFromAmmQuoteForParlayAMM(
address market,
Position position,
uint amount
) external view returns (uint);
function updateParlayVolume(address _account, uint _amount) external;
function buyPriceImpact(
address market,
ISportsAMM.Position position,
uint amount
) external view returns (int impact);
function obtainOdds(address _market, ISportsAMM.Position _position) external view returns (uint oddsToReturn);
function buyFromAmmQuoteWithDifferentCollateral(
address market,
ISportsAMM.Position position,
uint amount,
address collateral
) external view returns (uint collateralQuote, uint sUSDToPay);
function availableToBuyFromAMMWithBaseOdds(
address market,
ISportsAMM.Position position,
uint baseOdds,
uint balance,
bool useBalance
) external view returns (uint availableAmount);
function floorBaseOdds(uint baseOdds, address market) external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "../SportMarkets/Parlay/ParlayVerifier.sol";
interface IParlayMarketsAMM {
/* ========== VIEWS / VARIABLES ========== */
function parlaySize() external view returns (uint);
function sUSD() external view returns (IERC20Upgradeable);
function sportsAmm() external view returns (address);
function parlayPolicy() external view returns (address);
function parlayAmmFee() external view returns (uint);
function maxAllowedRiskPerCombination() external view returns (uint);
function maxSupportedOdds() external view returns (uint);
function getSgpFeePerCombination(
uint tag1,
uint tag2_1,
uint tag2_2,
uint position1,
uint position2
) external view returns (uint sgpFee);
function riskPerCombination(
address _sportMarkets1,
uint _position1,
address _sportMarkets2,
uint _position2,
address _sportMarkets3,
uint _position3,
address _sportMarkets4,
uint _position4
) external view returns (uint);
function riskPerGameCombination(
address _sportMarkets1,
address _sportMarkets2,
address _sportMarkets3,
address _sportMarkets4,
address _sportMarkets5,
address _sportMarkets6,
address _sportMarkets7,
address _sportMarkets8
) external view returns (uint);
function riskPerPackedGamesCombination(bytes32 gamesPacked) external view returns (uint);
function isActiveParlay(address _parlayMarket) external view returns (bool isActiveParlayMarket);
function exerciseParlay(address _parlayMarket) external;
function triggerResolvedEvent(address _account, bool _userWon) external;
function resolveParlay() external;
function buyFromParlay(
address[] calldata _sportMarkets,
uint[] calldata _positions,
uint _sUSDPaid,
uint _additionalSlippage,
uint _expectedPayout,
address _differentRecepient
) external;
function buyQuoteFromParlay(
address[] calldata _sportMarkets,
uint[] calldata _positions,
uint _sUSDPaid
)
external
view
returns (
uint sUSDAfterFees,
uint totalBuyAmount,
uint totalQuote,
uint initialQuote,
uint skewImpact,
uint[] memory finalQuotes,
uint[] memory amountsToBuy
);
function canCreateParlayMarket(
address[] calldata _sportMarkets,
uint[] calldata _positions,
uint _sUSDToPay
) external view returns (bool canBeCreated);
function numActiveParlayMarkets() external view returns (uint);
function activeParlayMarkets(uint index, uint pageSize) external view returns (address[] memory);
function parlayVerifier() external view returns (ParlayVerifier);
function minUSDAmount() external view returns (uint);
function maxSupportedAmount() external view returns (uint);
function safeBoxImpact() external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
import "../interfaces/IPositionalMarketManager.sol";
import "../interfaces/IPosition.sol";
import "../interfaces/IPriceFeed.sol";
interface ISportPositionalMarket {
/* ========== TYPES ========== */
enum Phase {
Trading,
Maturity,
Expiry
}
enum Side {
Cancelled,
Home,
Away,
Draw
}
/* ========== VIEWS / VARIABLES ========== */
function getOptions()
external
view
returns (
IPosition home,
IPosition away,
IPosition draw
);
function times() external view returns (uint maturity, uint destruction);
function getGameDetails() external view returns (bytes32 gameId, string memory gameLabel);
function getGameId() external view returns (bytes32);
function deposited() external view returns (uint);
function optionsCount() external view returns (uint);
function creator() external view returns (address);
function resolved() external view returns (bool);
function cancelled() external view returns (bool);
function paused() external view returns (bool);
function phase() external view returns (Phase);
function canResolve() external view returns (bool);
function result() external view returns (Side);
function isChild() external view returns (bool);
function optionsInitialized() external view returns (bool);
function tags(uint idx) external view returns (uint);
function getTags() external view returns (uint tag1, uint tag2);
function getTagsLength() external view returns (uint tagsLength);
function getParentMarketPositions() external view returns (IPosition position1, IPosition position2);
function getParentMarketPositionsUint() external view returns (uint position1, uint position2);
function getStampedOdds()
external
view
returns (
uint,
uint,
uint
);
function balancesOf(address account)
external
view
returns (
uint home,
uint away,
uint draw
);
function totalSupplies()
external
view
returns (
uint home,
uint away,
uint draw
);
function isDoubleChance() external view returns (bool);
function parentMarket() external view returns (ISportPositionalMarket);
/* ========== MUTATIVE FUNCTIONS ========== */
function setPaused(bool _paused) external;
function updateDates(uint256 _maturity, uint256 _expiry) external;
function mint(uint value) external;
function exerciseOptions() external;
function restoreInvalidOdds(
uint _homeOdds,
uint _awayOdds,
uint _drawOdds
) external;
function initializeOptions() external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
interface IStakingThales {
function updateVolume(address account, uint amount) external;
function updateStakingRewards(
uint _currentPeriodRewards,
uint _extraRewards,
uint _revShare
) external;
/* ========== VIEWS / VARIABLES ========== */
function totalStakedAmount() external view returns (uint);
function stakedBalanceOf(address account) external view returns (uint);
function currentPeriodRewards() external view returns (uint);
function currentPeriodFees() external view returns (uint);
function getLastPeriodOfClaimedRewards(address account) external view returns (uint);
function getRewardsAvailable(address account) external view returns (uint);
function getRewardFeesAvailable(address account) external view returns (uint);
function getAlreadyClaimedRewards(address account) external view returns (uint);
function getContractRewardFunds() external view returns (uint);
function getContractFeeFunds() external view returns (uint);
function getAMMVolume(address account) external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "../ParlayMarket.sol";
import "./ParlayAMMLiquidityPool.sol";
contract ParlayAMMLiquidityPoolRound {
/* ========== LIBRARIES ========== */
using SafeERC20Upgradeable for IERC20Upgradeable;
/* ========== STATE VARIABLES ========== */
ParlayAMMLiquidityPool public liquidityPool;
IERC20Upgradeable public sUSD;
uint public round;
uint public roundStartTime;
uint public roundEndTime;
/* ========== CONSTRUCTOR ========== */
bool public initialized;
function initialize(
address _liquidityPool,
IERC20Upgradeable _sUSD,
uint _round,
uint _roundStartTime,
uint _roundEndTime
) external {
require(!initialized, "Already initialized");
initialized = true;
liquidityPool = ParlayAMMLiquidityPool(_liquidityPool);
sUSD = _sUSD;
round = _round;
roundStartTime = _roundStartTime;
roundEndTime = _roundEndTime;
sUSD.approve(_liquidityPool, type(uint256).max);
}
function updateRoundTimes(uint _roundStartTime, uint _roundEndTime) external onlyLiquidityPool {
roundStartTime = _roundStartTime;
roundEndTime = _roundEndTime;
emit RoundTimesUpdated(_roundStartTime, _roundEndTime);
}
modifier onlyLiquidityPool() {
require(msg.sender == address(liquidityPool), "only the Pool manager may perform these methods");
_;
}
event RoundTimesUpdated(uint _roundStartTime, uint _roundEndTime);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Address.sol)
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
__Context_init_unchained();
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ISportAMMRiskManager {
function calculateCapToBeUsed(address _market) external view returns (uint toReturn);
function isTotalSpendingLessThanTotalRisk(uint _totalSpent, address _market) external view returns (bool _isNotRisky);
function isMarketForSportOnePositional(uint _tag) external view returns (bool);
function isMarketForPlayerPropsOnePositional(uint _tag) external view returns (bool);
function minSupportedOddsPerSport(uint tag) external view returns (uint);
function minSpreadPerSport(uint tag1, uint tag2) external view returns (uint);
function maxSpreadPerSport(uint tag) external view returns (uint);
function getMinSpreadToUse(
bool useDefaultMinSpread,
address market,
uint min_spread,
uint min_spreadPerAddress
) external view returns (uint);
function getMaxSpreadForMarket(address _market, uint max_spread) external view returns (uint);
function getMinOddsForMarket(address _market, uint minSupportedOdds) external view returns (uint minOdds);
function getCapAndMaxSpreadForMarket(address _market, uint max_spread) external view returns (uint, uint);
function getCapMaxSpreadAndMinOddsForMarket(
address _market,
uint max_spread,
uint minSupportedOdds
)
external
view
returns (
uint cap,
uint maxSpread,
uint minOddsForMarket
);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// interfaces
import "../../interfaces/IParlayMarketsAMM.sol";
import "../../interfaces/ISportsAMM.sol";
// import "../../interfaces/IParlayMarketData.sol";
import "../../interfaces/ISportPositionalMarket.sol";
import "../../interfaces/IParlayPolicy.sol";
contract ParlayVerifier {
uint private constant ONE = 1e18;
uint private constant ONE_PERCENT = 1e16;
uint private constant TAG_F1 = 9445;
uint private constant TAG_MOTOGP = 9497;
uint private constant TAG_GOLF = 100121;
uint private constant TAG_NUMBER_SPREAD = 10001;
uint private constant TAG_NUMBER_TOTAL = 10002;
uint private constant DOUBLE_CHANCE_TAG = 10003;
uint private constant PLAYER_PROPS_TAG = 10010;
struct InitialQuoteParameters {
address[] sportMarkets;
uint[] positions;
uint totalSUSDToPay;
uint parlaySize;
uint defaultONE;
uint[] sgpFees;
ISportsAMM sportsAMM;
address parlayAMM;
}
struct VerifyMarket {
address[] sportMarkets;
uint[] positions;
ISportsAMM sportsAMM;
address parlayAMM;
uint defaultONE;
}
struct CachedMarket {
bytes32 gameId;
uint gameCounter;
}
struct CheckSGP {
address[] sportMarkets;
uint[] positions;
uint[] tag1;
uint[] tag2;
IParlayPolicy parlayPolicy;
uint defaultONE;
}
/// @notice Verifying if given parlay is able to be created given the policies in state
/// @param params VerifyMarket parameters
/// @return eligible if the parlay can be created
/// @return odds the odds for each position
/// @return sgpFees the fees applied per position in case of SameGameParlay
function _verifyMarkets(VerifyMarket memory params)
internal
view
returns (
bool eligible,
uint[] memory odds,
uint[] memory sgpFees
)
{
eligible = true;
uint[] memory tags1;
uint[] memory tags2;
IParlayPolicy parlayPolicy = IParlayPolicy(IParlayMarketsAMM(params.parlayAMM).parlayPolicy());
(tags1, tags2) = _obtainAllTags(params.sportMarkets);
(odds, sgpFees) = _checkSGPAndGetOdds(
CheckSGP(params.sportMarkets, params.positions, tags1, tags2, parlayPolicy, params.defaultONE)
);
}
/// @notice Obtain all the tags for each position and calculate unique ones
/// @param sportMarkets the sport markets for the parlay
/// @return tag1 all the tags 1 per market
/// @return tag2 all the tags 2 per market
function _obtainAllTags(address[] memory sportMarkets) internal view returns (uint[] memory tag1, uint[] memory tag2) {
tag1 = new uint[](sportMarkets.length);
tag2 = new uint[](sportMarkets.length);
address sportMarket;
for (uint i = 0; i < sportMarkets.length; i++) {
// 1. Get all tags for a sport market (tag1, tag2)
sportMarket = sportMarkets[i];
tag1[i] = ISportPositionalMarket(sportMarket).tags(0);
tag2[i] = ISportPositionalMarket(sportMarket).getTagsLength() > 1
? ISportPositionalMarket(sportMarket).tags(1)
: 0;
for (uint j = 0; j < i; j++) {
if (sportMarkets[i] == sportMarkets[j]) {
revert("SameTeamOnParlay");
}
}
}
}
/// @notice Check the names, check if any markets are SGPs, obtain odds and apply fees if needed
/// @param params all the parameters to calculate the fees and odds per position
/// @return odds all the odds per position
/// @return sgpFees all the fees per position
function _checkSGPAndGetOdds(CheckSGP memory params) internal view returns (uint[] memory odds, uint[] memory sgpFees) {
odds = new uint[](params.sportMarkets.length);
sgpFees = new uint[](odds.length);
bool[] memory alreadyInSGP = new bool[](sgpFees.length);
for (uint i = 0; i < params.sportMarkets.length; i++) {
for (uint j = 0; j < i; j++) {
if (params.sportMarkets[j] != params.sportMarkets[i]) {
if (!alreadyInSGP[j] && params.tag1[j] == params.tag1[i] && (params.tag2[i] > 0 || params.tag2[j] > 0)) {
address parentI = address(ISportPositionalMarket(params.sportMarkets[i]).parentMarket());
address parentJ = address(ISportPositionalMarket(params.sportMarkets[j]).parentMarket());
if (
(params.tag2[j] > 0 && parentJ == params.sportMarkets[i]) ||
(params.tag2[i] > 0 && parentI == params.sportMarkets[j]) ||
// the following line is for totals + spreads or totals + playerProps
(params.tag2[i] > 0 && params.tag2[j] > 0 && parentI == parentJ)
) {
uint sgpFee = params.parlayPolicy.getSgpFeePerCombination(
IParlayPolicy.SGPData(
params.tag1[i],
params.tag2[i],
params.tag2[j],
params.positions[i],
params.positions[j]
)
);
if (params.tag2[j] == PLAYER_PROPS_TAG && params.tag2[i] == PLAYER_PROPS_TAG) {
// check if the markets are elibible props markets
if (
!params.parlayPolicy.areEligiblePropsMarkets(
params.sportMarkets[i],
params.sportMarkets[j],
params.tag1[i]
)
) {
revert("InvalidPlayerProps");
}
} else if (sgpFee == 0) {
revert("SameTeamOnParlay");
} else {
alreadyInSGP[i] = true;
alreadyInSGP[j] = true;
if (params.tag2[j] > 0) {
(odds[i], odds[j], sgpFees[i], sgpFees[j]) = _getSGPSingleOdds(
params.parlayPolicy.getMarketDefaultOdds(
params.sportMarkets[i],
params.positions[i]
),
params.parlayPolicy.getMarketDefaultOdds(
params.sportMarkets[j],
params.positions[j]
),
params.positions[j],
sgpFee,
params.parlayPolicy.getChildMarketTotalLine(params.sportMarkets[j]),
params.defaultONE
);
} else {
(odds[j], odds[i], sgpFees[j], sgpFees[i]) = _getSGPSingleOdds(
params.parlayPolicy.getMarketDefaultOdds(
params.sportMarkets[j],
params.positions[j]
),
params.parlayPolicy.getMarketDefaultOdds(
params.sportMarkets[i],
params.positions[i]
),
params.positions[i],
sgpFee,
params.parlayPolicy.getChildMarketTotalLine(params.sportMarkets[i]),
params.defaultONE
);
}
}
}
}
} else {
revert("SameTeamOnParlay");
}
}
if (odds[i] == 0) {
odds[i] = params.parlayPolicy.getMarketDefaultOdds(params.sportMarkets[i], params.positions[i]);
}
}
}
function getSPGOdds(
uint odds1,
uint odds2,
uint position2,
uint sgpFee,
uint totalsLine,
uint defaultONE
)
external
pure
returns (
uint resultOdds1,
uint resultOdds2,
uint sgpFee1,
uint sgpFee2
)
{
(resultOdds1, resultOdds2, sgpFee1, sgpFee2) = _getSGPSingleOdds(
odds1,
odds2,
position2,
sgpFee,
totalsLine,
defaultONE
);
}
/// @notice Calculate the sgpFees for the positions of two sport markets, given their odds and default sgpfee
/// @param odds1 the odd of position 1 (usually the moneyline odd)
/// @param odds2 the odd of position 2 (usually the totals/spreads odd)
/// @param sgpFee the default sgp fee
/// @return resultOdds1 the odd1
/// @return resultOdds2 the odd2
/// @return sgpFee1 the fee for position 1 or odd1
/// @return sgpFee2 the fee for position 2 or odd2
function _getSGPSingleOdds(
uint odds1,
uint odds2,
uint position2,
uint sgpFee,
uint totalsLine,
uint defaultONE
)
internal
pure
returns (
uint resultOdds1,
uint resultOdds2,
uint sgpFee1,
uint sgpFee2
)
{
resultOdds1 = odds1;
resultOdds2 = odds2;
odds1 = odds1 * defaultONE;
odds2 = odds2 * defaultONE;
if (odds1 > 0 && odds2 > 0) {
if (totalsLine == 2) {
sgpFee2 = sgpFee;
} else if (totalsLine == 250) {
if (position2 == 0) {
if (odds1 < (6 * ONE_PERCENT) && odds2 < (70 * ONE_PERCENT)) {
sgpFee2 = sgpFee - (ONE - sgpFee);
} else if (odds1 >= (99 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) - 1 * ONE_PERCENT);
} else if (odds1 >= (96 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 90 * ONE_PERCENT) / ONE;
} else if (odds1 >= (93 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 75 * ONE_PERCENT) / ONE;
} else if (odds1 >= (90 * ONE_PERCENT) && odds2 >= (65 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 90 * ONE_PERCENT) / ONE;
} else if (odds1 >= (83 * ONE_PERCENT) && odds2 >= (98 * ONE_PERCENT)) {
sgpFee2 = sgpFee + (5 * ONE_PERCENT);
} else if (odds1 >= (83 * ONE_PERCENT) && odds2 >= (52 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds1 >= (80 * ONE_PERCENT) && odds2 >= (74 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 20 * ONE_PERCENT) / ONE;
} else if (odds1 >= (80 * ONE_PERCENT) && odds2 >= (70 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 50 * ONE_PERCENT) / ONE;
} else if (odds1 >= (80 * ONE_PERCENT) && odds2 >= (60 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds1 >= (80 * ONE_PERCENT) && odds2 >= (50 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 90 * ONE_PERCENT) / ONE;
} else if (odds2 >= (60 * ONE_PERCENT) && odds1 <= (10 * ONE_PERCENT)) {
sgpFee2 = sgpFee;
} else if (odds2 >= (55 * ONE_PERCENT) && odds1 <= (19 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 70 * ONE_PERCENT) / ONE;
} else if (odds2 >= (55 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 40 * ONE_PERCENT) / ONE;
} else if (odds2 >= (54 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 70 * ONE_PERCENT) / ONE;
} else if (odds2 >= (52 * ONE_PERCENT)) {
if (odds1 <= (10 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 50 * ONE_PERCENT) / ONE;
} else if (odds1 <= (23 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 45 * ONE_PERCENT) / ONE;
} else if (odds1 <= (46 * ONE_PERCENT)) {
sgpFee2 = sgpFee + (((5 * ONE_PERCENT) * (ONE - odds1)) / ONE);
} else {
sgpFee2 = sgpFee;
}
} else if (odds2 >= (51 * ONE_PERCENT) && odds1 <= (20 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 90 * ONE_PERCENT) / ONE;
} else if (odds2 >= (51 * ONE_PERCENT) && odds1 <= (25 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((sgpFee * 10 * ONE_PERCENT) / ONE);
} else if (odds2 >= (50 * ONE_PERCENT)) {
if (odds1 < (10 * ONE_PERCENT)) {
sgpFee2 = ONE + odds1;
} else if (odds1 <= (21 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 90 * ONE_PERCENT) / ONE;
} else if (odds1 <= (23 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds1 <= (56 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 70 * ONE_PERCENT) / ONE;
} else {
uint oddsDiff = odds2 > odds1 ? odds2 - odds1 : odds1 - odds2;
if (oddsDiff > 0) {
oddsDiff = (oddsDiff - (5 * ONE_PERCENT) / (90 * ONE_PERCENT));
oddsDiff = ((ONE - sgpFee) * oddsDiff) / ONE;
sgpFee2 = (sgpFee * (ONE + oddsDiff)) / ONE;
} else {
sgpFee2 = sgpFee;
}
}
} else if (odds2 >= (49 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 70 * ONE_PERCENT) / ONE;
} else if (odds2 >= (48 * ONE_PERCENT) && odds1 <= (20 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds2 >= (48 * ONE_PERCENT) && odds1 <= (40 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 20 * ONE_PERCENT) / ONE;
} else if (odds2 >= (48 * ONE_PERCENT) && odds1 <= (50 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 40 * ONE_PERCENT) / ONE;
} else if (odds2 >= (48 * ONE_PERCENT)) {
sgpFee2 = sgpFee;
} else if (odds2 >= (46 * ONE_PERCENT) && odds1 <= (43 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 50 * ONE_PERCENT) / ONE;
} else if (odds2 >= (46 * ONE_PERCENT)) {
sgpFee2 = sgpFee;
} else if (odds2 >= (43 * ONE_PERCENT)) {
if (odds1 <= (24 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (46 * ONE_PERCENT)) {
sgpFee2 = sgpFee > 5 * ONE_PERCENT ? sgpFee - (2 * ONE_PERCENT) : sgpFee;
} else {
uint oddsDiff = odds2 > odds1 ? odds2 - odds1 : odds1 - odds2;
if (oddsDiff > 0) {
oddsDiff = (oddsDiff - (5 * ONE_PERCENT) / (90 * ONE_PERCENT));
oddsDiff = ((ONE - sgpFee + (ONE - sgpFee) / 2) * oddsDiff) / ONE;
sgpFee2 = (sgpFee * (ONE + oddsDiff)) / ONE;
} else {
sgpFee2 = sgpFee;
}
}
} else if (odds2 >= (39 * ONE_PERCENT) && odds1 >= (43 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 70 * ONE_PERCENT) / ONE;
} else if (odds2 >= (35 * ONE_PERCENT)) {
if (odds1 <= (46 * ONE_PERCENT)) {
sgpFee2 = sgpFee - (((ONE - sgpFee) * (ONE - odds1)) / ONE);
} else {
sgpFee2 = sgpFee > 5 * ONE_PERCENT ? sgpFee - (2 * ONE_PERCENT) : sgpFee;
}
} else if (odds2 < (35 * ONE_PERCENT)) {
if (odds1 <= (46 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 90 * ONE_PERCENT) / ONE;
} else {
sgpFee2 = sgpFee > 5 * ONE_PERCENT ? sgpFee - (2 * ONE_PERCENT) : sgpFee;
}
}
} else {
if (odds2 >= (56 * ONE_PERCENT)) {
if (odds2 > (68 * ONE_PERCENT) && odds1 <= (15 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 50 * ONE_PERCENT) / ONE;
} else if (odds1 >= 76 * ONE_PERCENT) {
sgpFee2 = (ONE + (15 * ONE_PERCENT) + (odds1 * 15 * ONE_PERCENT) / ONE);
} else if (odds1 >= 60 * ONE_PERCENT) {
sgpFee2 = ONE + (ONE - sgpFee);
} else if (odds2 >= (58 * ONE_PERCENT) && odds1 <= (18 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 80 * ONE_PERCENT) / ONE;
} else if (odds2 >= (58 * ONE_PERCENT) && odds1 <= (32 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 70 * ONE_PERCENT) / ONE;
} else if (odds2 >= (55 * ONE_PERCENT) && odds1 >= (58 * ONE_PERCENT)) {
sgpFee2 = sgpFee;
} else if (odds2 >= (55 * ONE_PERCENT) && odds1 <= (18 * ONE_PERCENT)) {
sgpFee2 = sgpFee;
} else if (odds1 <= 35 * ONE_PERCENT && odds1 >= 30 * ONE_PERCENT) {
sgpFee2 = sgpFee;
} else if (odds1 <= 15 * ONE_PERCENT) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 50 * ONE_PERCENT) / ONE;
} else {
sgpFee2 = (ONE + (15 * ONE_PERCENT) + (odds1 * 10 * ONE_PERCENT) / ONE);
}
} else if (odds2 <= (32 * ONE_PERCENT) && odds1 >= (65 * ONE_PERCENT)) {
sgpFee2 = sgpFee - (ONE - sgpFee);
} else if (odds2 <= (32 * ONE_PERCENT) && odds1 >= (85 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 80 * ONE_PERCENT) / ONE;
} else if (odds2 <= (35 * ONE_PERCENT) && odds1 <= (125 * 1e15)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 10 * ONE_PERCENT) / ONE;
} else if (odds2 <= (35 * ONE_PERCENT) && odds1 > (125 * 1e15) && odds1 <= (13 * ONE_PERCENT)) {
sgpFee2 = sgpFee;
} else if (odds2 <= (35 * ONE_PERCENT) && odds1 <= (15 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 20 * ONE_PERCENT) / ONE;
} else if (odds2 <= (35 * ONE_PERCENT) && odds1 <= (24 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (37 * ONE_PERCENT) && odds1 <= (10 * ONE_PERCENT)) {
sgpFee2 = sgpFee - (ONE - sgpFee);
} else if (odds2 <= (37 * ONE_PERCENT) && odds1 <= (16 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 60 * ONE_PERCENT) / ONE;
} else if (odds2 <= (38 * ONE_PERCENT) && odds1 >= (80 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee + 5 * ONE_PERCENT);
} else if (odds2 <= (38 * ONE_PERCENT) && odds1 >= (70 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee + 10 * ONE_PERCENT);
} else if (odds2 <= (38 * ONE_PERCENT) && odds1 >= (66 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee + 25 * ONE_PERCENT);
} else if (odds2 <= (38 * ONE_PERCENT) && odds1 >= (50 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee + 5 * ONE_PERCENT);
} else if (odds2 <= (38 * ONE_PERCENT) && odds1 >= (25 * ONE_PERCENT)) {
sgpFee2 = ONE + (2 * (ONE - sgpFee));
} else if (odds2 <= (38 * ONE_PERCENT) && odds1 >= (23 * ONE_PERCENT)) {
sgpFee2 = sgpFee - ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (38 * ONE_PERCENT) && odds1 < (23 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 40 * ONE_PERCENT) / ONE;
} else if (odds2 <= (39 * ONE_PERCENT) && odds1 < (20 * ONE_PERCENT)) {
sgpFee2 = sgpFee - (sgpFee * 20 * ONE_PERCENT) / ONE;
} else if (odds2 <= (40 * ONE_PERCENT) && odds1 <= (9 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 80 * ONE_PERCENT) / ONE;
} else if (odds2 <= (40 * ONE_PERCENT) && odds1 <= (11 * ONE_PERCENT)) {
sgpFee2 = sgpFee;
} else if (odds2 <= (40 * ONE_PERCENT) && odds1 <= (13 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 50 * ONE_PERCENT) / ONE;
} else if (odds2 <= (40 * ONE_PERCENT) && odds1 <= (14 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 80 * ONE_PERCENT) / ONE;
} else if (odds2 <= (40 * ONE_PERCENT) && odds1 <= (30 * ONE_PERCENT)) {
sgpFee2 = ONE + (sgpFee * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (40 * ONE_PERCENT) && odds1 <= (54 * ONE_PERCENT)) {
sgpFee2 = ONE + (sgpFee * 20 * ONE_PERCENT) / ONE;
} else if (odds2 <= (40 * ONE_PERCENT) && odds1 > (54 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee);
} else if (odds2 <= (43 * ONE_PERCENT) && odds1 <= (11 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 50 * ONE_PERCENT) / ONE;
} else if (odds2 <= (43 * ONE_PERCENT) && odds1 <= (12 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 75 * ONE_PERCENT) / ONE;
} else if (odds2 <= (43 * ONE_PERCENT) && odds1 <= (14 * ONE_PERCENT)) {
sgpFee2 = sgpFee + (sgpFee * 20 * ONE_PERCENT) / ONE;
} else if (odds2 <= (43 * ONE_PERCENT) && odds1 <= (15 * ONE_PERCENT)) {
sgpFee2 = sgpFee - (sgpFee * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (43 * ONE_PERCENT) && odds1 <= (51 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee);
} else if (odds2 <= (44 * ONE_PERCENT) && odds1 >= (55 * ONE_PERCENT)) {
sgpFee2 = ONE + (sgpFee * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (44 * ONE_PERCENT) && odds1 <= (55 * ONE_PERCENT)) {
sgpFee2 = ONE + (sgpFee * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (45 * ONE_PERCENT) && odds1 >= (70 * ONE_PERCENT)) {
sgpFee2 = ONE + (2 * (ONE - sgpFee)) - ((ONE - sgpFee) * 40 * ONE_PERCENT) / ONE;
} else if (odds2 <= (45 * ONE_PERCENT) && odds1 >= (44 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 70 * ONE_PERCENT) / ONE;
} else if (odds2 <= (45 * ONE_PERCENT) && odds1 >= (40 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 10 * ONE_PERCENT) / ONE;
} else if (odds2 <= (45 * ONE_PERCENT) && odds1 >= (20 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 80 * ONE_PERCENT) / ONE;
} else if (odds2 <= (45 * ONE_PERCENT) && odds1 < (20 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (47 * ONE_PERCENT) && odds1 <= (17 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 70 * ONE_PERCENT) / ONE;
} else if (odds2 <= (47 * ONE_PERCENT) && odds1 <= (23 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 8 * ONE_PERCENT) / ONE;
} else if (odds2 <= (48 * ONE_PERCENT) && odds1 <= (11 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 80 * ONE_PERCENT) / ONE;
} else if (odds2 <= (48 * ONE_PERCENT) && odds1 <= (24 * ONE_PERCENT)) {
sgpFee2 = sgpFee + (sgpFee * 20 * ONE_PERCENT) / ONE;
} else if (odds2 <= (49 * ONE_PERCENT) && odds1 <= (15 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (49 * ONE_PERCENT) && odds1 <= (25 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 80 * ONE_PERCENT) / ONE;
} else if (odds2 <= (49 * ONE_PERCENT) && odds1 <= (33 * ONE_PERCENT)) {
sgpFee2 = sgpFee;
} else if (odds2 <= (50 * ONE_PERCENT) && odds1 <= (10 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 50 * ONE_PERCENT) / ONE;
} else if (odds2 <= (50 * ONE_PERCENT) && odds1 <= (17 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 35 * ONE_PERCENT) / ONE;
} else if (odds2 <= (50 * ONE_PERCENT) && odds1 <= (20 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee);
} else if (odds2 <= (50 * ONE_PERCENT) && odds1 <= (25 * ONE_PERCENT)) {
sgpFee2 = sgpFee - (ONE - sgpFee) - (ONE - sgpFee);
} else if (odds2 <= (51 * ONE_PERCENT) && odds1 <= (24 * ONE_PERCENT)) {
sgpFee2 = sgpFee;
} else if (odds2 <= (52 * ONE_PERCENT) && odds1 <= (10 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 35 * ONE_PERCENT) / ONE;
} else if (odds2 <= (52 * ONE_PERCENT) && odds1 <= (15 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 45 * ONE_PERCENT) / ONE;
} else if (odds2 <= (52 * ONE_PERCENT) && odds1 <= (24 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 35 * ONE_PERCENT) / ONE;
} else if (odds2 <= (52 * ONE_PERCENT) && odds1 > (72 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 35 * ONE_PERCENT) / ONE;
} else if (odds2 <= (53 * ONE_PERCENT) && odds1 <= (24 * ONE_PERCENT)) {
sgpFee2 = ONE + (sgpFee * 30 * ONE_PERCENT) / ONE;
} else if (odds2 <= (53 * ONE_PERCENT) && odds1 <= (40 * ONE_PERCENT)) {
sgpFee2 = ONE;
} else if (odds2 < (54 * ONE_PERCENT) && odds1 >= (74 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee);
} else if (odds2 < (54 * ONE_PERCENT) && odds1 >= (58 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee + 10 * ONE_PERCENT);
} else if (odds2 < (54 * ONE_PERCENT) && odds1 >= (24 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee);
} else if (odds2 < (54 * ONE_PERCENT) && odds1 < (24 * ONE_PERCENT)) {
sgpFee2 = sgpFee + ((ONE - sgpFee) * 30 * ONE_PERCENT) / ONE;
} else if (odds2 < (56 * ONE_PERCENT) && odds1 >= (82 * ONE_PERCENT)) {
sgpFee2 = ONE + ((ONE - sgpFee) * 50 * ONE_PERCENT) / ONE;
} else if (odds2 < (56 * ONE_PERCENT) && odds1 >= (40 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee);
} else if (odds2 < (56 * ONE_PERCENT) && odds1 >= (10 * ONE_PERCENT)) {
sgpFee2 = ONE + (ONE - sgpFee);
} else {
sgpFee2 = sgpFee;
}
}
} else {
sgpFee2 = sgpFee;
}
if (sgpFee2 > 0) {
uint totalQuote = (odds1 * odds2) / ONE;
uint totalQuoteWSGP = ((totalQuote * ONE * ONE) / sgpFee2) / ONE;
if (totalQuoteWSGP < (10 * ONE_PERCENT)) {
if (odds1 > (10 * ONE_PERCENT)) {
sgpFee2 = ((totalQuote * ONE * ONE) / (10 * ONE_PERCENT)) / ONE;
} else {
sgpFee2 = ((totalQuote * ONE * ONE) / (odds1 - ((odds1 * 10 * ONE_PERCENT) / ONE))) / ONE;
}
} else if (totalQuoteWSGP > odds1 && odds1 < odds2) {
sgpFee2 = odds2 + (4 * 1e15);
} else if (totalQuoteWSGP > odds2 && odds2 <= odds1) {
sgpFee2 = odds1 + (4 * 1e15);
}
}
}
}
function calculateInitialQuotesForParlay(InitialQuoteParameters memory params)
external
view
returns (
uint totalQuote,
uint totalBuyAmount,
uint skewImpact,
uint[] memory finalQuotes,
uint[] memory amountsToBuy
)
{
uint numOfMarkets = params.sportMarkets.length;
uint inverseSum;
bool eligible;
amountsToBuy = new uint[](numOfMarkets);
(eligible, finalQuotes, params.sgpFees) = _verifyMarkets(
VerifyMarket(params.sportMarkets, params.positions, params.sportsAMM, params.parlayAMM, params.defaultONE)
);
if (eligible && numOfMarkets == params.positions.length && numOfMarkets > 0 && numOfMarkets <= params.parlaySize) {
for (uint i = 0; i < numOfMarkets; i++) {
if (params.positions[i] > 2) {
totalQuote = 0;
break;
}
if (finalQuotes.length == 0) {
totalQuote = 0;
break;
}
if (finalQuotes[i] == 0) {
totalQuote = 0;
break;
}
finalQuotes[i] = (params.defaultONE * finalQuotes[i]);
if (params.sgpFees[i] > 0) {
finalQuotes[i] = ((finalQuotes[i] * ONE * ONE) / params.sgpFees[i]) / ONE;
}
totalQuote = totalQuote == 0 ? finalQuotes[i] : (totalQuote * finalQuotes[i]) / ONE;
}
if (totalQuote != 0) {
if (totalQuote < IParlayMarketsAMM(params.parlayAMM).maxSupportedOdds()) {
totalQuote = IParlayMarketsAMM(params.parlayAMM).maxSupportedOdds();
}
totalBuyAmount = (params.totalSUSDToPay * ONE) / totalQuote;
_calculateRisk(params.sportMarkets, (totalBuyAmount - params.totalSUSDToPay), params.sportsAMM.parlayAMM());
}
for (uint i = 0; i < numOfMarkets; i++) {
//consider if this works well for Arbitrum at 6 decimals
if (finalQuotes[i] > 0) {
amountsToBuy[i] = (ONE * params.totalSUSDToPay) / finalQuotes[i];
}
}
}
}
function obtainSportsAMMPosition(uint _position) public pure returns (ISportsAMM.Position) {
if (_position == 0) {
return ISportsAMM.Position.Home;
} else if (_position == 1) {
return ISportsAMM.Position.Away;
}
return ISportsAMM.Position.Draw;
}
function _calculateRisk(
address[] memory _sportMarkets,
uint _sUSDInRisky,
address _parlayAMM
) internal view returns (bool riskFree) {
require(_checkRisk(_sportMarkets, _sUSDInRisky, _parlayAMM), "RiskPerComb exceeded");
riskFree = true;
}
function _checkRisk(
address[] memory _sportMarkets,
uint _sUSDInRisk,
address _parlayAMM
) internal view returns (bool riskFree) {
if (_sportMarkets.length > 1 && _sportMarkets.length <= IParlayMarketsAMM(_parlayAMM).parlaySize()) {
uint riskCombination = IParlayMarketsAMM(_parlayAMM).riskPerPackedGamesCombination(
_calculateCombinationKey(_sportMarkets)
);
riskFree = (riskCombination + _sUSDInRisk) <= IParlayMarketsAMM(_parlayAMM).maxAllowedRiskPerCombination();
}
}
function _calculateCombinationKey(address[] memory _sportMarkets) internal pure returns (bytes32) {
address[] memory sortedAddresses = new address[](_sportMarkets.length);
sortedAddresses = _sort(_sportMarkets);
return keccak256(abi.encodePacked(sortedAddresses));
}
function _sort(address[] memory data) internal pure returns (address[] memory) {
_quickSort(data, int(0), int(data.length - 1));
return data;
}
function _quickSort(
address[] memory arr,
int left,
int right
) internal pure {
int i = left;
int j = right;
if (i == j) return;
address pivot = arr[uint(left + (right - left) / 2)];
while (i <= j) {
while (arr[uint(i)] < pivot) i++;
while (pivot < arr[uint(j)]) j--;
if (i <= j) {
(arr[uint(i)], arr[uint(j)]) = (arr[uint(j)], arr[uint(i)]);
i++;
j--;
}
}
if (left < j) _quickSort(arr, left, j);
if (i < right) _quickSort(arr, i, right);
}
function sort(address[] memory data) external pure returns (address[] memory) {
_quickSort(data, int(0), int(data.length - 1));
return data;
}
function calculateCombinationKey(address[] memory _sportMarkets) external pure returns (bytes32) {
return _calculateCombinationKey(_sportMarkets);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IParlayPolicy {
struct SGPData {
uint tag1;
uint tag2_1;
uint tag2_2;
uint position1;
uint position2;
}
/* ========== VIEWS / VARIABLES ========== */
function consumer() external view returns (address);
function getSgpFeePerCombination(SGPData memory params) external view returns (uint sgpFee);
function getMarketDefaultOdds(address _sportMarket, uint _position) external view returns (uint odd);
function areEligiblePropsMarkets(
address _childMarket1,
address _childMarket2,
uint _tag1
) external view returns (bool samePlayerDifferentProp);
function getChildMarketTotalLine(address _sportMarket) external view returns (uint childTotalsLine);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
import "../interfaces/IPositionalMarket.sol";
interface IPositionalMarketManager {
/* ========== VIEWS / VARIABLES ========== */
function durations() external view returns (uint expiryDuration, uint maxTimeToMaturity);
function capitalRequirement() external view returns (uint);
function marketCreationEnabled() external view returns (bool);
function onlyAMMMintingAndBurning() external view returns (bool);
function transformCollateral(uint value) external view returns (uint);
function reverseTransformCollateral(uint value) external view returns (uint);
function totalDeposited() external view returns (uint);
function numActiveMarkets() external view returns (uint);
function activeMarkets(uint index, uint pageSize) external view returns (address[] memory);
function numMaturedMarkets() external view returns (uint);
function maturedMarkets(uint index, uint pageSize) external view returns (address[] memory);
function isActiveMarket(address candidate) external view returns (bool);
function isKnownMarket(address candidate) external view returns (bool);
function getThalesAMM() external view returns (address);
/* ========== MUTATIVE FUNCTIONS ========== */
function createMarket(
bytes32 oracleKey,
uint strikePrice,
uint maturity,
uint initialMint // initial sUSD to mint options for,
) external returns (IPositionalMarket);
function resolveMarket(address market) external;
function expireMarkets(address[] calldata market) external;
function transferSusdTo(
address sender,
address receiver,
uint amount
) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
import "./IPositionalMarket.sol";
interface IPosition {
/* ========== VIEWS / VARIABLES ========== */
function getBalanceOf(address account) external view returns (uint);
function getTotalSupply() external view returns (uint);
function exerciseWithAmount(address claimant, uint amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
interface IPriceFeed {
// Structs
struct RateAndUpdatedTime {
uint216 rate;
uint40 time;
}
// Mutative functions
function addAggregator(bytes32 currencyKey, address aggregatorAddress) external;
function removeAggregator(bytes32 currencyKey) external;
// Views
function rateForCurrency(bytes32 currencyKey) external view returns (uint);
function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time);
function getRates() external view returns (uint[] memory);
function getCurrencies() external view returns (bytes32[] memory);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
import "../interfaces/IPositionalMarketManager.sol";
import "../interfaces/IPosition.sol";
import "../interfaces/IPriceFeed.sol";
interface IPositionalMarket {
/* ========== TYPES ========== */
enum Phase {
Trading,
Maturity,
Expiry
}
enum Side {
Up,
Down
}
/* ========== VIEWS / VARIABLES ========== */
function getOptions() external view returns (IPosition up, IPosition down);
function times() external view returns (uint maturity, uint destructino);
function getOracleDetails()
external
view
returns (
bytes32 key,
uint strikePrice,
uint finalPrice
);
function fees() external view returns (uint poolFee, uint creatorFee);
function deposited() external view returns (uint);
function creator() external view returns (address);
function resolved() external view returns (bool);
function phase() external view returns (Phase);
function oraclePrice() external view returns (uint);
function oraclePriceAndTimestamp() external view returns (uint price, uint updatedAt);
function canResolve() external view returns (bool);
function result() external view returns (Side);
function balancesOf(address account) external view returns (uint up, uint down);
function totalSupplies() external view returns (uint up, uint down);
function getMaximumBurnable(address account) external view returns (uint amount);
/* ========== MUTATIVE FUNCTIONS ========== */
function mint(uint value) external;
function exerciseOptions() external returns (uint);
function burnOptions(uint amount) external;
function burnOptionsMaximum() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../OwnedWithInit.sol";
import "@openzeppelin/contracts-4.4.1/token/ERC20/utils/SafeERC20.sol";
// Internal references
import "../../interfaces/IParlayMarketsAMM.sol";
import "../SportPositions/SportPosition.sol";
import "../../interfaces/ISportPositionalMarket.sol";
import "../../interfaces/ISportPositionalMarketManager.sol";
contract ParlayMarket is OwnedWithInit {
using SafeERC20 for IERC20;
uint private constant ONE = 1e18;
uint private constant ONE_PERCENT = 1e16;
uint private constant TWELVE_DECIMAL = 1e6;
enum Phase {
Trading,
Maturity,
Expiry
}
struct SportMarkets {
address sportAddress;
uint position;
uint odd;
uint result;
bool resolved;
bool exercised;
bool hasWon;
bool isCancelled;
}
IParlayMarketsAMM public parlayMarketsAMM;
address public parlayOwner;
uint public expiry;
uint public amount;
uint public sUSDPaid;
uint public totalResultQuote;
uint public numOfSportMarkets;
bool public resolved;
bool public paused;
bool public parlayAlreadyLost;
bool public initialized;
mapping(uint => SportMarkets) public sportMarket;
mapping(address => uint) private _sportMarketIndex;
/* ========== CONSTRUCTOR ========== */
function initialize(
address[] calldata _sportMarkets,
uint[] calldata _positionPerMarket,
uint _amount,
uint _sUSDPaid,
uint _expiryDuration,
address _parlayMarketsAMM,
address _parlayOwner,
uint _totalQuote,
uint[] calldata _marketQuotes
) external {
require(!initialized, "Parlay Market already initialized");
initialized = true;
initOwner(msg.sender);
parlayMarketsAMM = IParlayMarketsAMM(_parlayMarketsAMM);
require(_sportMarkets.length == _positionPerMarket.length, "Lengths not matching");
numOfSportMarkets = _sportMarkets.length;
for (uint i = 0; i < numOfSportMarkets; i++) {
sportMarket[i].sportAddress = _sportMarkets[i];
sportMarket[i].position = _positionPerMarket[i];
sportMarket[i].odd = _marketQuotes[i];
_sportMarketIndex[_sportMarkets[i]] = i + 1;
}
amount = _amount;
expiry = _expiryDuration;
sUSDPaid = _sUSDPaid;
parlayOwner = _parlayOwner;
totalResultQuote = _totalQuote;
}
//===================== VIEWS ===========================
function isParlayLost() public view returns (bool) {
bool marketWinning;
bool marketResolved;
bool hasPendingWinningMarkets;
for (uint i = 0; i < numOfSportMarkets; i++) {
(marketWinning, marketResolved) = _isWinningPosition(sportMarket[i].sportAddress, sportMarket[i].position);
if (marketResolved && !marketWinning) {
return true;
}
}
return false;
}
function areAllPositionsResolved() public view returns (bool) {
for (uint i = 0; i < numOfSportMarkets; i++) {
if (!ISportPositionalMarket(sportMarket[i].sportAddress).resolved()) {
return false;
}
}
return true;
}
function isUserTheWinner() external view returns (bool hasUserWon) {
if (areAllPositionsResolved()) {
hasUserWon = !isParlayLost();
}
}
function phase() public view returns (Phase) {
if (resolved) {
if (resolved && expiry < block.timestamp) {
return Phase.Expiry;
} else {
return Phase.Maturity;
}
} else {
return Phase.Trading;
}
}
//exercisedOrExercisableMarkets left for legacy support
function isParlayExercisable() public view returns (bool isExercisable, bool[] memory exercisedOrExercisableMarkets) {
isExercisable = !resolved && (areAllPositionsResolved() || isParlayLost());
}
//============================== UPDATE PARAMETERS ===========================
function setPaused(bool _paused) external onlyAMM {
require(paused != _paused, "State not changed");
paused = _paused;
emit PauseUpdated(_paused);
}
//============================== EXERCISE ===================================
function exerciseWiningSportMarkets() external onlyAMM {
require(!paused, "Market paused");
(bool isExercisable, ) = isParlayExercisable();
require(isExercisable, "Parlay not exercisable yet");
uint totalSUSDamount = parlayMarketsAMM.sUSD().balanceOf(address(this));
if (isParlayLost()) {
if (totalSUSDamount > 0) {
parlayMarketsAMM.sUSD().transfer(address(parlayMarketsAMM), totalSUSDamount);
}
} else {
uint finalPayout = parlayMarketsAMM.sUSD().balanceOf(address(this));
for (uint i = 0; i < numOfSportMarkets; i++) {
address _sportMarket = sportMarket[i].sportAddress;
ISportPositionalMarket currentSportMarket = ISportPositionalMarket(_sportMarket);
uint result = uint(currentSportMarket.result());
if (result == 0) {
finalPayout = (finalPayout * sportMarket[i].odd) / ONE;
}
}
parlayMarketsAMM.sUSD().transfer(address(parlayOwner), finalPayout);
parlayMarketsAMM.sUSD().transfer(address(parlayMarketsAMM), parlayMarketsAMM.sUSD().balanceOf(address(this)));
}
_resolve(!isParlayLost());
}
//============================== INTERNAL FUNCTIONS ===================================
function _resolve(bool _userWon) internal {
parlayAlreadyLost = !_userWon;
resolved = true;
parlayMarketsAMM.triggerResolvedEvent(parlayOwner, _userWon);
emit Resolved(_userWon);
}
function _isWinningPosition(address _sportMarket, uint _userPosition)
internal
view
returns (bool isWinning, bool isResolved)
{
ISportPositionalMarket currentSportMarket = ISportPositionalMarket(_sportMarket);
isResolved = currentSportMarket.resolved();
if (
isResolved &&
(uint(currentSportMarket.result()) == (_userPosition + 1) ||
currentSportMarket.result() == ISportPositionalMarket.Side.Cancelled)
) {
isWinning = true;
}
}
//============================== ON EXPIRY FUNCTIONS ===================================
function withdrawCollateral(address recipient) external onlyAMM {
parlayMarketsAMM.sUSD().transfer(recipient, parlayMarketsAMM.sUSD().balanceOf(address(this)));
}
function expire(address payable beneficiary) external onlyAMM {
require(phase() == Phase.Expiry, "Ticket Expired");
require(!resolved, "Can't expire resolved parlay.");
emit Expired(beneficiary);
_selfDestruct(beneficiary);
}
function _selfDestruct(address payable beneficiary) internal {
uint balance = parlayMarketsAMM.sUSD().balanceOf(address(this));
if (balance != 0) {
parlayMarketsAMM.sUSD().transfer(beneficiary, balance);
}
// Destroy the option tokens before destroying the market itself.
// selfdestruct(beneficiary);
}
modifier onlyAMM() {
require(msg.sender == address(parlayMarketsAMM), "only the AMM may perform these methods");
_;
}
event Resolved(bool isUserTheWinner);
event Expired(address beneficiary);
event PauseUpdated(bool _paused);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract OwnedWithInit {
address public owner;
address public nominatedOwner;
constructor() {}
function initOwner(address _owner) internal {
require(owner == address(0), "Init can only be called when owner is 0");
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
modifier onlyOwner {
_onlyOwner();
_;
}
function _onlyOwner() private view {
require(msg.sender == owner, "Only the contract owner may perform this action");
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../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;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Inheritance
import "@openzeppelin/contracts-4.4.1/token/ERC20/IERC20.sol";
import "../../interfaces/IPosition.sol";
// Libraries
import "@openzeppelin/contracts-4.4.1/utils/math/SafeMath.sol";
// Internal references
import "./SportPositionalMarket.sol";
contract SportPosition is IERC20, IPosition {
/* ========== LIBRARIES ========== */
using SafeMath for uint;
/* ========== STATE VARIABLES ========== */
string public name;
string public symbol;
uint8 public constant decimals = 18;
SportPositionalMarket public market;
mapping(address => uint) public override balanceOf;
uint public override totalSupply;
// The argument order is allowance[owner][spender]
mapping(address => mapping(address => uint)) private allowances;
// Enforce a 1 cent minimum amount
uint internal constant _MINIMUM_AMOUNT = 1e16;
address public sportsAMM;
/* ========== CONSTRUCTOR ========== */
bool public initialized = false;
function initialize(
string calldata _name,
string calldata _symbol,
address _sportsAMM
) external {
require(!initialized, "Positional Market already initialized");
initialized = true;
name = _name;
symbol = _symbol;
market = SportPositionalMarket(msg.sender);
// add through constructor
sportsAMM = _sportsAMM;
}
function allowance(address owner, address spender) external view override returns (uint256) {
if (spender == sportsAMM) {
return 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
} else {
return allowances[owner][spender];
}
}
function _requireMinimumAmount(uint amount) internal pure returns (uint) {
require(amount >= _MINIMUM_AMOUNT || amount == 0, "Balance < $0.01");
return amount;
}
function mint(address minter, uint amount) external onlyMarket {
_requireMinimumAmount(amount);
totalSupply = totalSupply.add(amount);
balanceOf[minter] = balanceOf[minter].add(amount); // Increment rather than assigning since a transfer may have occurred.
emit Transfer(address(0), minter, amount);
emit Issued(minter, amount);
}
// This must only be invoked after maturity.
function exercise(address claimant) external onlyMarket {
uint balance = balanceOf[claimant];
if (balance == 0) {
return;
}
balanceOf[claimant] = 0;
totalSupply = totalSupply.sub(balance);
emit Transfer(claimant, address(0), balance);
emit Burned(claimant, balance);
}
// This must only be invoked after maturity.
function exerciseWithAmount(address claimant, uint amount) external override onlyMarket {
require(amount > 0, "Can not exercise zero amount!");
require(balanceOf[claimant] >= amount, "Balance must be greather or equal amount that is burned");
balanceOf[claimant] = balanceOf[claimant] - amount;
totalSupply = totalSupply.sub(amount);
emit Transfer(claimant, address(0), amount);
emit Burned(claimant, amount);
}
// This must only be invoked after the exercise window is complete.
// Note that any options which have not been exercised will linger.
function expire(address payable beneficiary) external onlyMarket {
selfdestruct(beneficiary);
}
/* ---------- ERC20 Functions ---------- */
function _transfer(
address _from,
address _to,
uint _value
) internal returns (bool success) {
market.requireUnpaused();
require(_to != address(0) && _to != address(this), "Invalid address");
uint fromBalance = balanceOf[_from];
require(_value <= fromBalance, "Insufficient balance");
balanceOf[_from] = fromBalance.sub(_value);
balanceOf[_to] = balanceOf[_to].add(_value);
emit Transfer(_from, _to, _value);
return true;
}
function transfer(address _to, uint _value) external override returns (bool success) {
return _transfer(msg.sender, _to, _value);
}
function transferFrom(
address _from,
address _to,
uint _value
) external override returns (bool success) {
if (msg.sender != sportsAMM) {
uint fromAllowance = allowances[_from][msg.sender];
require(_value <= fromAllowance, "Insufficient allowance");
allowances[_from][msg.sender] = fromAllowance.sub(_value);
}
return _transfer(_from, _to, _value);
}
function approve(address _spender, uint _value) external override returns (bool success) {
require(_spender != address(0));
allowances[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function getBalanceOf(address account) external view override returns (uint) {
return balanceOf[account];
}
function getTotalSupply() external view override returns (uint) {
return totalSupply;
}
/* ========== MODIFIERS ========== */
modifier onlyMarket() {
require(msg.sender == address(market), "Only market allowed");
_;
}
/* ========== EVENTS ========== */
event Issued(address indexed account, uint value);
event Burned(address indexed account, uint value);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../interfaces/ISportPositionalMarket.sol";
interface ISportPositionalMarketManager {
/* ========== VIEWS / VARIABLES ========== */
function marketCreationEnabled() external view returns (bool);
function totalDeposited() external view returns (uint);
function numActiveMarkets() external view returns (uint);
function activeMarkets(uint index, uint pageSize) external view returns (address[] memory);
function numMaturedMarkets() external view returns (uint);
function maturedMarkets(uint index, uint pageSize) external view returns (address[] memory);
function isActiveMarket(address candidate) external view returns (bool);
function isDoubleChanceMarket(address candidate) external view returns (bool);
function doesSportSupportDoubleChance(uint _sport) external view returns (bool);
function isDoubleChanceSupported() external view returns (bool);
function isKnownMarket(address candidate) external view returns (bool);
function getActiveMarketAddress(uint _index) external view returns (address);
function transformCollateral(uint value) external view returns (uint);
function reverseTransformCollateral(uint value) external view returns (uint);
function isMarketPaused(address _market) external view returns (bool);
function expiryDuration() external view returns (uint);
function isWhitelistedAddress(address _address) external view returns (bool);
function getOddsObtainer() external view returns (address obtainer);
/* ========== MUTATIVE FUNCTIONS ========== */
function createMarket(
bytes32 gameId,
string memory gameLabel,
uint maturity,
uint initialMint, // initial sUSD to mint options for,
uint positionCount,
uint[] memory tags,
bool isChild,
address parentMarket
) external returns (ISportPositionalMarket);
function setMarketPaused(address _market, bool _paused) external;
function updateDatesForMarket(address _market, uint256 _newStartTime) external;
function resolveMarket(address market, uint outcome) external;
function expireMarkets(address[] calldata market) external;
function transferSusdTo(
address sender,
address receiver,
uint amount
) external;
function queryMintsAndMaturityStatusForPlayerProps(address[] memory _playerPropsMarkets)
external
view
returns (
bool[] memory _hasAnyMintsArray,
bool[] memory _isMaturedArray,
bool[] memory _isResolvedArray,
uint[] memory _maturities
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @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.
*/
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].
*/
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 v4.7.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
* now has built in overflow checking.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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.
*
* _Available since v3.4._
*/
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 addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Inheritance
import "../../OwnedWithInit.sol";
import "../../interfaces/ISportPositionalMarket.sol";
import "../../interfaces/ITherundownConsumer.sol";
import "../../interfaces/ISportsAMM.sol";
import "../../interfaces/ISportPositionalMarketFactory.sol";
// Libraries
import "@openzeppelin/contracts-4.4.1/utils/math/SafeMath.sol";
// Internal references
import "./SportPositionalMarketManager.sol";
import "./SportPosition.sol";
import "@openzeppelin/contracts-4.4.1/token/ERC20/IERC20.sol";
contract SportPositionalMarket is OwnedWithInit, ISportPositionalMarket {
/* ========== LIBRARIES ========== */
using SafeMath for uint;
/* ========== TYPES ========== */
struct Options {
SportPosition home;
SportPosition away;
SportPosition draw;
}
struct Times {
uint maturity;
uint expiry;
}
struct GameDetails {
bytes32 gameId;
string gameLabel;
}
struct SportPositionalMarketParameters {
address owner;
address creator;
bytes32 gameId;
string gameLabel;
uint[2] times; // [maturity, expiry]
uint positionCount;
address[] positions;
uint[] tags;
bool isChild;
address parentMarket;
bool isDoubleChance;
address factory;
}
/* ========== STATE VARIABLES ========== */
Options public options;
uint public override optionsCount;
Times public parentTimes;
GameDetails private gameDetails;
string private childGameLabel;
uint[] public override tags;
uint public finalResult;
// `deposited` tracks the sum of all deposits.
// This must explicitly be kept, in case tokens are transferred to the contract directly.
uint public override deposited;
address public override creator;
bool public override resolved;
bool public override cancelled;
uint public cancelTimestamp;
uint public homeOddsOnCancellation;
uint public awayOddsOnCancellation;
uint public drawOddsOnCancellation;
bool public invalidOdds;
bool public initialized = false;
bool public override paused;
bool public override isChild;
ISportPositionalMarket public override parentMarket;
bool public override isDoubleChance;
bool public override optionsInitialized;
address public factory;
/* ========== CONSTRUCTOR ========== */
function initialize(SportPositionalMarketParameters calldata _parameters) external {
require(!initialized, "Positional Market already initialized");
initialized = true;
initOwner(_parameters.owner);
optionsCount = _parameters.positionCount;
require(optionsCount == _parameters.positions.length, "Position count mismatch");
creator = _parameters.creator;
if (_parameters.isChild || _parameters.isDoubleChance) {
isChild = _parameters.isChild;
isDoubleChance = _parameters.isDoubleChance;
parentMarket = ISportPositionalMarket(_parameters.parentMarket);
childGameLabel = _parameters.gameLabel;
} else {
parentTimes = Times(_parameters.times[0], _parameters.times[1]);
gameDetails = GameDetails(_parameters.gameId, _parameters.gameLabel);
}
tags = _parameters.tags;
factory = _parameters.factory;
}
/* ---------- External Contracts ---------- */
function _manager() internal view returns (SportPositionalMarketManager) {
return SportPositionalMarketManager(owner);
}
/* ---------- Phases ---------- */
function _times() internal view returns (Times memory) {
if (isChild || isDoubleChance) {
(uint maturity, uint expiry) = parentMarket.times();
return Times(maturity, expiry);
}
return parentTimes;
}
function _matured() internal view returns (bool) {
return _times().maturity < block.timestamp;
}
function _expired() internal view returns (bool) {
return resolved && (_times().expiry < block.timestamp || deposited == 0);
}
function _isPaused() internal view returns (bool) {
return isDoubleChance ? parentMarket.paused() : paused;
}
function getTags() external view override returns (uint tag1, uint tag2) {
if (tags.length > 1) {
tag1 = tags[0];
tag2 = tags[1];
} else {
tag1 = tags[0];
}
}
function getTagsLength() external view override returns (uint tagsLength) {
return tags.length;
}
function times() external view override returns (uint maturity, uint destruction) {
Times memory time = _times();
return (time.maturity, time.expiry);
}
function phase() external view override returns (Phase) {
if (!_matured()) {
return Phase.Trading;
}
if (!_expired()) {
return Phase.Maturity;
}
return Phase.Expiry;
}
function setPaused(bool _paused) external override onlyOwner managerNotPaused {
require(paused != _paused, "State not changed");
paused = _paused;
emit PauseUpdated(_paused);
}
function updateDates(uint256 _maturity, uint256 _expiry) external override onlyOwner managerNotPaused noDoubleChance {
require(_maturity > block.timestamp, "Maturity must be in a future");
if (!isChild) {
parentTimes = Times(_maturity, _expiry);
}
emit DatesUpdated(_maturity, _expiry);
}
/* ---------- Market Resolution ---------- */
function canResolve() public view override returns (bool) {
return !resolved && _matured() && !paused;
}
function getGameDetails() external view override returns (bytes32 gameId, string memory gameLabel) {
return (_getDetails().gameId, _getDetails().gameLabel);
}
function getParentMarketPositionsUint() public view override returns (uint position1, uint position2) {
if (isDoubleChance) {
(IPosition home, , ) = parentMarket.getOptions();
if (_hasNotBeenInitialized(home)) {
if (
keccak256(abi.encodePacked(_getDetails().gameLabel)) == keccak256(abi.encodePacked("HomeTeamNotToLose"))
) {
(position1, position2) = (0, 2);
} else if (
keccak256(abi.encodePacked(_getDetails().gameLabel)) == keccak256(abi.encodePacked("AwayTeamNotToLose"))
) {
(position1, position2) = (1, 2);
} else {
(position1, position2) = (0, 1);
}
}
}
}
function getParentMarketPositions() public view override returns (IPosition position1, IPosition position2) {
if (isDoubleChance) {
(IPosition home, IPosition away, IPosition draw) = parentMarket.getOptions();
if (keccak256(abi.encodePacked(_getDetails().gameLabel)) == keccak256(abi.encodePacked("HomeTeamNotToLose"))) {
(position1, position2) = (home, draw);
} else if (
keccak256(abi.encodePacked(_getDetails().gameLabel)) == keccak256(abi.encodePacked("AwayTeamNotToLose"))
) {
(position1, position2) = (away, draw);
} else {
(position1, position2) = (home, away);
}
}
}
function _result() internal view returns (Side) {
if (!resolved || cancelled) {
return Side.Cancelled;
} else if (finalResult == 3 && optionsCount > 2) {
return Side.Draw;
} else {
return finalResult == 1 ? Side.Home : Side.Away;
}
}
function result() external view override returns (Side) {
return _result();
}
/* ---------- Option Balances and Mints ---------- */
function getGameId() external view override returns (bytes32) {
return _getDetails().gameId;
}
function getStampedOdds()
public
view
override
returns (
uint,
uint,
uint
)
{
if (cancelled) {
if (isDoubleChance) {
(uint position1Odds, uint position2Odds) = _getParentPositionOdds();
return (position1Odds + position2Odds, 0, 0);
}
return (homeOddsOnCancellation, awayOddsOnCancellation, drawOddsOnCancellation);
} else {
return (0, 0, 0);
}
}
function _getParentPositionOdds() internal view returns (uint odds1, uint odds2) {
(uint homeOddsParent, uint awayOddsParent, uint drawOddsParent) = parentMarket.getStampedOdds();
(IPosition position1, IPosition position2) = getParentMarketPositions();
(IPosition home, IPosition away, ) = parentMarket.getOptions();
if (_hasNotBeenInitialized(home)) {
return (0, 0);
}
odds1 = position1 == home ? homeOddsParent : position1 == away ? awayOddsParent : drawOddsParent;
odds2 = position2 == home ? homeOddsParent : position2 == away ? awayOddsParent : drawOddsParent;
}
function _balancesOf(address account)
internal
view
returns (
uint home,
uint away,
uint draw
)
{
if (!optionsInitialized) {
return (0, 0, 0);
}
if (optionsCount > 2) {
return (
options.home.getBalanceOf(account),
options.away.getBalanceOf(account),
options.draw.getBalanceOf(account)
);
}
return (options.home.getBalanceOf(account), options.away.getBalanceOf(account), 0);
}
function balancesOf(address account)
external
view
override
returns (
uint home,
uint away,
uint draw
)
{
return _balancesOf(account);
}
function totalSupplies()
external
view
override
returns (
uint home,
uint away,
uint draw
)
{
if (optionsCount > 2) {
return (options.home.totalSupply(), options.away.totalSupply(), options.draw.totalSupply());
}
return (options.home.totalSupply(), options.away.totalSupply(), 0);
}
function getOptions()
external
view
override
returns (
IPosition home,
IPosition away,
IPosition draw
)
{
home = options.home;
away = options.away;
draw = options.draw;
}
function _getMaximumBurnable(address account) internal view returns (uint amount) {
(uint homeBalance, uint awayBalance, uint drawBalance) = _balancesOf(account);
uint min = homeBalance;
if (min > awayBalance) {
min = awayBalance;
if (optionsCount > 2 && drawBalance < min) {
min = drawBalance;
}
} else {
if (optionsCount > 2 && drawBalance < min) {
min = drawBalance;
}
}
return min;
}
function _getDetails() internal view returns (GameDetails memory) {
if (isChild || isDoubleChance) {
(bytes32 gameId, ) = parentMarket.getGameDetails();
return GameDetails(gameId, childGameLabel);
}
return gameDetails;
}
/* ---------- Utilities ---------- */
function _incrementDeposited(uint value) internal returns (uint _deposited) {
_deposited = deposited.add(value);
deposited = _deposited;
_manager().incrementTotalDeposited(value);
}
function _decrementDeposited(uint value) internal returns (uint _deposited) {
_deposited = deposited.sub(value);
deposited = _deposited;
_manager().decrementTotalDeposited(value);
}
function _requireManagerNotPaused() internal view {
require(!_manager().paused(), "This action cannot be performed while the contract is paused");
}
function requireUnpaused() external view {
_requireManagerNotPaused();
}
/* ========== MUTATIVE FUNCTIONS ========== */
/* ---------- Minting ---------- */
function mint(uint value) external override {
require(!_matured() && !_isPaused(), "Minting inactive");
require(msg.sender == ISportPositionalMarketFactory(factory).sportsAMM(), "Invalid minter");
if (value == 0) {
return;
}
_mint(msg.sender, value);
if (!isDoubleChance) {
_incrementDeposited(value);
_manager().transferSusdTo(msg.sender, address(this), value);
}
}
function _mint(address minter, uint amount) internal {
if (!optionsInitialized) {
_initializeOptions();
}
if (isDoubleChance) {
options.home.mint(minter, amount);
emit Mint(Side.Home, minter, amount);
} else {
options.home.mint(minter, amount);
options.away.mint(minter, amount);
emit Mint(Side.Home, minter, amount);
emit Mint(Side.Away, minter, amount);
if (optionsCount > 2) {
options.draw.mint(minter, amount);
emit Mint(Side.Draw, minter, amount);
}
}
}
function initializeOptions() external override {
_initializeOptions();
}
function _initializeOptions() internal {
require(!optionsInitialized, "already initialized");
address[] memory positions = new address[](optionsCount);
for (uint i = 0; i < optionsCount; i++) {
positions[i] = address(SportPosition(Clones.clone(ISportPositionalMarketFactory(factory).positionMastercopy())));
}
// Instantiate the options themselves
options.home = SportPosition(positions[0]);
options.away = SportPosition(positions[1]);
if (isChild) {
require(tags.length > 1, "Child markets must have more then one tag");
if (tags[1] == 10001) {
options.home.initialize(_getDetails().gameLabel, "HOME", ISportPositionalMarketFactory(factory).sportsAMM());
options.away.initialize(_getDetails().gameLabel, "AWAY", ISportPositionalMarketFactory(factory).sportsAMM());
} else if (tags[1] == 10002 || tags[1] == 10010) {
options.home.initialize(_getDetails().gameLabel, "OVER", ISportPositionalMarketFactory(factory).sportsAMM());
options.away.initialize(
_getDetails().gameLabel,
"UNDER",
ISportPositionalMarketFactory(factory).sportsAMM()
);
}
} else {
options.home.initialize(_getDetails().gameLabel, "HOME", ISportPositionalMarketFactory(factory).sportsAMM());
options.away.initialize(_getDetails().gameLabel, "AWAY", ISportPositionalMarketFactory(factory).sportsAMM());
}
if (optionsCount > 2) {
options.draw = SportPosition(positions[2]);
options.draw.initialize(_getDetails().gameLabel, "DRAW", ISportPositionalMarketFactory(factory).sportsAMM());
}
optionsInitialized = true;
emit PositionsInitialized(
address(this),
address(options.home),
address(options.away),
optionsCount > 2 ? address(options.draw) : address(0)
);
}
function _hasNotBeenInitialized(IPosition home) internal view returns (bool) {
return address(home) == address(0);
}
/* ---------- Market Resolution ---------- */
function resolve(uint _outcome) external onlyOwner managerNotPaused {
require(_outcome <= optionsCount, "Invalid outcome");
if (_outcome == 0) {
cancelled = true;
cancelTimestamp = block.timestamp;
if (!isDoubleChance) {
stampOdds();
}
} else {
require(canResolve(), "Can not resolve market");
}
finalResult = _outcome;
resolved = true;
emit MarketResolved(_result(), deposited, 0, 0);
}
function stampOdds() internal {
uint[] memory odds = new uint[](optionsCount);
odds = ITherundownConsumer(creator).getNormalizedOddsForMarket(address(this));
if (odds[0] == 0 || odds[1] == 0) {
invalidOdds = true;
}
homeOddsOnCancellation = odds[0];
awayOddsOnCancellation = odds[1];
drawOddsOnCancellation = optionsCount > 2 ? odds[2] : 0;
emit StoredOddsOnCancellation(homeOddsOnCancellation, awayOddsOnCancellation, drawOddsOnCancellation);
}
/* ---------- Claiming and Exercising Options ---------- */
function exerciseOptions() external override {
// The market must be resolved if it has not been.
require(resolved, "Unresolved");
require(!_isPaused(), "Paused");
// If the account holds no options, revert.
(uint homeBalance, uint awayBalance, uint drawBalance) = _balancesOf(msg.sender);
require(homeBalance != 0 || awayBalance != 0 || drawBalance != 0, "Nothing to exercise");
if (isDoubleChance && _canExerciseParentOptions()) {
parentMarket.exerciseOptions();
}
// Each option only needs to be exercised if the account holds any of it.
if (homeBalance != 0) {
options.home.exercise(msg.sender);
}
if (awayBalance != 0) {
options.away.exercise(msg.sender);
}
if (drawBalance != 0) {
options.draw.exercise(msg.sender);
}
uint payout = _getPayout(homeBalance, awayBalance, drawBalance);
if (cancelled) {
require(
block.timestamp > cancelTimestamp.add(_manager().cancelTimeout()) && !invalidOdds,
"Unexpired timeout/ invalid odds"
);
payout = calculatePayoutOnCancellation(homeBalance, awayBalance, drawBalance);
}
emit OptionsExercised(msg.sender, payout);
if (payout != 0) {
if (!isDoubleChance) {
_decrementDeposited(payout);
}
payout = _manager().transformCollateral(payout);
ISportsAMM(ISportPositionalMarketFactory(factory).sportsAMM()).sUSD().transfer(msg.sender, payout);
}
}
function _canExerciseParentOptions() internal view returns (bool) {
if (!parentMarket.resolved() && !parentMarket.canResolve()) {
return false;
}
(uint homeBalance, uint awayBalance, uint drawBalance) = parentMarket.balancesOf(address(this));
if (homeBalance == 0 && awayBalance == 0 && drawBalance == 0) {
return false;
}
return true;
}
function _getPayout(
uint homeBalance,
uint awayBalance,
uint drawBalance
) internal view returns (uint payout) {
if (isDoubleChance) {
if (_result() == Side.Home) {
payout = homeBalance;
}
} else {
payout = (_result() == Side.Home) ? homeBalance : awayBalance;
if (optionsCount > 2 && _result() != Side.Home) {
payout = _result() == Side.Away ? awayBalance : drawBalance;
}
}
}
function restoreInvalidOdds(
uint _homeOdds,
uint _awayOdds,
uint _drawOdds
) external override onlyOwner {
require(_homeOdds > 0 && _awayOdds > 0, "Invalid odd");
homeOddsOnCancellation = _homeOdds;
awayOddsOnCancellation = _awayOdds;
drawOddsOnCancellation = optionsCount > 2 ? _drawOdds : 0;
invalidOdds = false;
emit StoredOddsOnCancellation(homeOddsOnCancellation, awayOddsOnCancellation, drawOddsOnCancellation);
}
function calculatePayoutOnCancellation(
uint _homeBalance,
uint _awayBalance,
uint _drawBalance
) public view returns (uint payout) {
if (!cancelled) {
return 0;
} else {
if (isDoubleChance) {
(uint position1Odds, uint position2Odds) = _getParentPositionOdds();
payout = _homeBalance.mul(position1Odds).div(1e18);
payout = payout.add(_homeBalance.mul(position2Odds).div(1e18));
} else {
payout = _homeBalance.mul(homeOddsOnCancellation).div(1e18);
payout = payout.add(_awayBalance.mul(awayOddsOnCancellation).div(1e18));
payout = payout.add(_drawBalance.mul(drawOddsOnCancellation).div(1e18));
}
}
}
/* ---------- Market Expiry ---------- */
function _selfDestruct(address payable beneficiary) internal {
uint _deposited = deposited;
if (_deposited != 0) {
_decrementDeposited(_deposited);
}
// Transfer the balance rather than the deposit value in case there are any synths left over
// from direct transfers.
uint balance = ISportsAMM(ISportPositionalMarketFactory(factory).sportsAMM()).sUSD().balanceOf(address(this));
if (balance != 0) {
ISportsAMM(ISportPositionalMarketFactory(factory).sportsAMM()).sUSD().transfer(beneficiary, balance);
}
// Destroy the option tokens before destroying the market itself.
options.home.expire(beneficiary);
options.away.expire(beneficiary);
selfdestruct(beneficiary);
}
function expire(address payable beneficiary) external onlyOwner {
require(_expired(), "Unexpired options remaining");
emit Expired(beneficiary);
_selfDestruct(beneficiary);
}
/* ========== MODIFIERS ========== */
modifier managerNotPaused() {
_requireManagerNotPaused();
_;
}
modifier noDoubleChance() {
require(!isDoubleChance, "Not supported for double chance markets");
_;
}
/* ========== EVENTS ========== */
event Mint(Side side, address indexed account, uint value);
event MarketResolved(Side result, uint deposited, uint poolFees, uint creatorFees);
event OptionsExercised(address indexed account, uint value);
event OptionsBurned(address indexed account, uint value);
event Expired(address beneficiary);
event StoredOddsOnCancellation(uint homeOdds, uint awayOdds, uint drawOdds);
event PauseUpdated(bool _paused);
event DatesUpdated(uint256 _maturity, uint256 _expiry);
event PositionsInitialized(address _market, address _home, address _away, address _draw);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ITherundownConsumer {
struct GameCreate {
bytes32 gameId;
uint256 startTime;
int24 homeOdds;
int24 awayOdds;
int24 drawOdds;
string homeTeam;
string awayTeam;
}
// view functions
function supportedSport(uint _sportId) external view returns (bool);
function gameOnADate(bytes32 _gameId) external view returns (uint);
function isGameResolvedOrCanceled(bytes32 _gameId) external view returns (bool);
function getNormalizedOddsForMarket(address _market) external view returns (uint[] memory);
function getGamesPerDatePerSport(uint _sportId, uint _date) external view returns (bytes32[] memory);
function getGamePropsForOdds(address _market)
external
view
returns (
uint,
uint,
bytes32
);
function gameIdPerMarket(address _market) external view returns (bytes32);
function getGameCreatedById(bytes32 _gameId) external view returns (GameCreate memory);
function isChildMarket(address _market) external view returns (bool);
function gameFulfilledCreated(bytes32 _gameId) external view returns (bool);
function playerProps() external view returns (address);
function oddsObtainer() external view returns (address);
// write functions
function fulfillGamesCreated(
bytes32 _requestId,
bytes[] memory _games,
uint _sportsId,
uint _date
) external;
function fulfillGamesResolved(
bytes32 _requestId,
bytes[] memory _games,
uint _sportsId
) external;
function fulfillGamesOdds(bytes32 _requestId, bytes[] memory _games) external;
function setPausedByCanceledStatus(address _market, bool _flag) external;
function setGameIdPerChildMarket(bytes32 _gameId, address _child) external;
function pauseOrUnpauseMarket(address _market, bool _pause) external;
function pauseOrUnpauseMarketForPlayerProps(
address _market,
bool _pause,
bool _invalidOdds,
bool _circuitBreakerMain
) external;
function setChildMarkets(
bytes32 _gameId,
address _main,
address _child,
bool _isSpread,
int16 _spreadHome,
uint24 _totalOver
) external;
function resolveMarketManually(
address _market,
uint _outcome,
uint8 _homeScore,
uint8 _awayScore,
bool _usebackupOdds
) external;
function getOddsForGame(bytes32 _gameId)
external
view
returns (
int24,
int24,
int24
);
function sportsIdPerGame(bytes32 _gameId) external view returns (uint);
function getGameStartTime(bytes32 _gameId) external view returns (uint256);
function getLastUpdatedFromGameResolve(bytes32 _gameId) external view returns (uint40);
function marketPerGameId(bytes32 _gameId) external view returns (address);
function marketResolved(address _market) external view returns (bool);
function marketCanceled(address _market) external view returns (bool);
function invalidOdds(address _market) external view returns (bool);
function isPausedByCanceledStatus(address _market) external view returns (bool);
function isSportOnADate(uint _date, uint _sportId) external view returns (bool);
function isSportTwoPositionsSport(uint _sportsId) external view returns (bool);
function marketForTeamName(string memory _teamName) external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ISportPositionalMarketFactory {
function sportsAMM() external view returns (address);
function positionMastercopy() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Inheritance
import "../../utils/proxy/solidity-0.8.0/ProxyOwned.sol";
import "../../utils/proxy/solidity-0.8.0/ProxyPausable.sol";
// Libraries
import "../../utils/libraries/AddressSetLib.sol";
import "@openzeppelin/contracts-4.4.1/utils/math/SafeMath.sol";
// Internal references
import "./SportPositionalMarketFactory.sol";
import "./SportPositionalMarket.sol";
import "./SportPosition.sol";
import "../../interfaces/ISportPositionalMarketManager.sol";
import "../../interfaces/ISportPositionalMarket.sol";
import "../../interfaces/ITherundownConsumer.sol";
import "@openzeppelin/contracts-4.4.1/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../../interfaces/IGamesOddsObtainer.sol";
import "../../interfaces/IGamesPlayerProps.sol";
import "../../interfaces/IGameChildMarket.sol";
contract SportPositionalMarketManager is Initializable, ProxyOwned, ProxyPausable, ISportPositionalMarketManager {
/* ========== LIBRARIES ========== */
using SafeMath for uint;
using AddressSetLib for AddressSetLib.AddressSet;
/* ========== STATE VARIABLES ========== */
uint public override expiryDuration;
bool public override marketCreationEnabled;
bool public customMarketCreationEnabled;
uint public override totalDeposited;
AddressSetLib.AddressSet internal _activeMarkets;
AddressSetLib.AddressSet internal _maturedMarkets;
SportPositionalMarketManager internal _migratingManager;
IERC20 public sUSD;
address public theRundownConsumer;
address public sportPositionalMarketFactory;
bool public needsTransformingCollateral;
mapping(address => bool) public whitelistedAddresses;
address private apexConsumer; // deprecated
uint public cancelTimeout;
mapping(address => bool) public whitelistedCancelAddresses;
address public oddsObtainer;
mapping(address => bool) public isDoubleChance;
bool public override isDoubleChanceSupported;
mapping(address => address[]) public doubleChanceMarketsByParent;
mapping(uint => bool) public override doesSportSupportDoubleChance;
address public playerProps;
/* ========== CONSTRUCTOR ========== */
function initialize(address _owner, IERC20 _sUSD) external initializer {
setOwner(_owner);
sUSD = _sUSD;
// Temporarily change the owner so that the setters don't revert.
owner = msg.sender;
marketCreationEnabled = true;
customMarketCreationEnabled = false;
}
/* ========== SETTERS ========== */
function setSportPositionalMarketFactory(address _sportPositionalMarketFactory) external onlyOwner {
sportPositionalMarketFactory = _sportPositionalMarketFactory;
emit SetSportPositionalMarketFactory(_sportPositionalMarketFactory);
}
function setTherundownConsumer(address _theRundownConsumer) external onlyOwner {
theRundownConsumer = _theRundownConsumer;
emit SetTherundownConsumer(_theRundownConsumer);
}
function setOddsObtainer(address _oddsObtainer) external onlyOwner {
oddsObtainer = _oddsObtainer;
emit SetObtainerAddress(_oddsObtainer);
}
function setPlayerProps(address _playerProps) external onlyOwner {
playerProps = _playerProps;
emit SetPlayerPropsAddress(_playerProps);
}
function getOddsObtainer() external view override returns (address obtainer) {
obtainer = oddsObtainer;
}
/// @notice setNeedsTransformingCollateral sets needsTransformingCollateral value
/// @param _needsTransformingCollateral boolen value to be set
function setNeedsTransformingCollateral(bool _needsTransformingCollateral) external onlyOwner {
needsTransformingCollateral = _needsTransformingCollateral;
}
/// @notice setWhitelistedAddresses enables whitelist addresses of given array
/// @param _whitelistedAddresses array of whitelisted addresses
/// @param _flag adding or removing from whitelist (true: add, false: remove)
function setWhitelistedAddresses(
address[] calldata _whitelistedAddresses,
bool _flag,
uint8 _group
) external onlyOwner {
require(_whitelistedAddresses.length > 0, "Whitelisted addresses cannot be empty");
for (uint256 index = 0; index < _whitelistedAddresses.length; index++) {
// only if current flag is different, if same skip it
if (_group == 1) {
if (whitelistedAddresses[_whitelistedAddresses[index]] != _flag) {
whitelistedAddresses[_whitelistedAddresses[index]] = _flag;
emit AddedIntoWhitelist(_whitelistedAddresses[index], _flag);
}
}
if (_group == 2) {
if (whitelistedCancelAddresses[_whitelistedAddresses[index]] != _flag) {
whitelistedCancelAddresses[_whitelistedAddresses[index]] = _flag;
emit AddedIntoWhitelist(_whitelistedAddresses[index], _flag);
}
}
}
}
/* ========== VIEWS ========== */
/* ---------- Market Information ---------- */
function isKnownMarket(address candidate) public view override returns (bool) {
return _activeMarkets.contains(candidate) || _maturedMarkets.contains(candidate);
}
function isActiveMarket(address candidate) public view override returns (bool) {
return _activeMarkets.contains(candidate) && !ISportPositionalMarket(candidate).paused();
}
function isDoubleChanceMarket(address candidate) public view override returns (bool) {
return isDoubleChance[candidate];
}
function numActiveMarkets() external view override returns (uint) {
return _activeMarkets.elements.length;
}
function activeMarkets(uint index, uint pageSize) external view override returns (address[] memory) {
return _activeMarkets.getPage(index, pageSize);
}
function numMaturedMarkets() external view override returns (uint) {
return _maturedMarkets.elements.length;
}
function getActiveMarketAddress(uint _index) external view override returns (address) {
if (_index < _activeMarkets.elements.length) {
return _activeMarkets.elements[_index];
} else {
return address(0);
}
}
function getDoubleChanceMarketsByParentMarket(address market) external view returns (address[] memory) {
return _getDoubleChanceMarkets(market);
}
function maturedMarkets(uint index, uint pageSize) external view override returns (address[] memory) {
return _maturedMarkets.getPage(index, pageSize);
}
function setMarketPaused(address _market, bool _paused) external override {
require(
msg.sender == owner ||
msg.sender == theRundownConsumer ||
msg.sender == oddsObtainer ||
msg.sender == playerProps ||
whitelistedAddresses[msg.sender],
"Invalid caller"
);
require(ISportPositionalMarket(_market).paused() != _paused, "No state change");
ISportPositionalMarket(_market).setPaused(_paused);
}
function updateDatesForMarket(address _market, uint256 _newStartTime) external override {
require(msg.sender == owner || msg.sender == theRundownConsumer, "Invalid caller");
uint expiry = _newStartTime.add(expiryDuration);
// Update main market
_updateDatesForMarket(_market, _newStartTime, expiry);
// Update child markets
_updateDatesForChildMarkets(_market, _newStartTime, expiry, oddsObtainer);
_updateDatesForChildMarkets(_market, _newStartTime, expiry, playerProps);
}
function _updateDatesForChildMarkets(
address _market,
uint256 _newStartTime,
uint256 _expiry,
address _childMarketContract
) internal {
uint numberOfChildMarkets = IGameChildMarket(_childMarketContract).numberOfChildMarkets(_market);
for (uint i = 0; i < numberOfChildMarkets; i++) {
address child = IGameChildMarket(_childMarketContract).mainMarketChildMarketIndex(_market, i);
_updateDatesForMarket(child, _newStartTime, _expiry);
}
}
function isMarketPaused(address _market) external view override returns (bool) {
return ISportPositionalMarket(_market).paused();
}
function queryMintsAndMaturityStatusForParents(address[] memory _parents)
external
view
returns (
bool[] memory _hasAnyMintsArray,
bool[] memory _isMaturedArray,
bool[] memory _isResolvedArray
)
{
_hasAnyMintsArray = new bool[](_parents.length);
_isMaturedArray = new bool[](_parents.length);
_isResolvedArray = new bool[](_parents.length);
for (uint i = 0; i < _parents.length; i++) {
(bool _hasAnyMints, uint _maturity) = _hasAnyMintsAndMaturityDatesForMarket(_parents[i]);
_isResolvedArray[i] = _isResolvedMarket(_parents[i]);
_hasAnyMintsArray[i] = _hasAnyMints;
_isMaturedArray[i] = _maturity <= block.timestamp;
}
}
function queryMintsAndMaturityStatusForPlayerProps(address[] memory _playerPropsMarkets)
external
view
override
returns (
bool[] memory _hasAnyMintsArray,
bool[] memory _isMaturedArray,
bool[] memory _isResolvedArray,
uint[] memory _maturities
)
{
_hasAnyMintsArray = new bool[](_playerPropsMarkets.length);
_isMaturedArray = new bool[](_playerPropsMarkets.length);
_isResolvedArray = new bool[](_playerPropsMarkets.length);
_maturities = new uint[](_playerPropsMarkets.length);
for (uint i = 0; i < _playerPropsMarkets.length; i++) {
(bool _hasAnyMints, uint _maturity) = _hasAnyMintsAndMaturityDatesForPP(_playerPropsMarkets[i]);
_isResolvedArray[i] = _isResolvedMarket(_playerPropsMarkets[i]);
_hasAnyMintsArray[i] = _hasAnyMints;
_isMaturedArray[i] = _maturity <= block.timestamp;
_maturities[i] = _maturity;
}
}
function _hasAnyMintsAndMaturityDatesForMarket(address _parent)
internal
view
returns (bool _hasAnyMints, uint _maturity)
{
ISportPositionalMarket marketContract = ISportPositionalMarket(_parent);
(uint maturity, ) = marketContract.times();
_hasAnyMints = marketContract.optionsInitialized() || _hasAnyMintsForChildren(_parent);
_maturity = maturity;
}
function _hasAnyMintsAndMaturityDatesForPP(address _market) internal view returns (bool _hasAnyMints, uint _maturity) {
ISportPositionalMarket marketContract = ISportPositionalMarket(_market);
(uint maturity, ) = marketContract.times();
_hasAnyMints = marketContract.optionsInitialized();
_maturity = maturity;
}
function _isResolvedMarket(address _market) internal view returns (bool _flag) {
return ISportPositionalMarket(_market).resolved();
}
function _getDoubleChanceMarkets(address market) internal view returns (address[] memory markets) {
uint length = doubleChanceMarketsByParent[market].length;
if (length > 0) {
markets = new address[](length);
for (uint i = 0; i < length; i++) {
markets[i] = doubleChanceMarketsByParent[market][i];
}
}
}
/* ========== MUTATIVE FUNCTIONS ========== */
/* ---------- Setters ---------- */
function setExpiryDuration(uint _expiryDuration) public onlyOwner {
expiryDuration = _expiryDuration;
emit ExpiryDurationUpdated(_expiryDuration);
}
function setsUSD(address _address) external onlyOwner {
sUSD = IERC20(_address);
emit SetsUSD(_address);
}
/* ---------- Deposit Management ---------- */
function incrementTotalDeposited(uint delta) external onlyActiveMarkets notPaused {
totalDeposited = totalDeposited.add(delta);
}
function decrementTotalDeposited(uint delta) external onlyKnownMarkets notPaused {
// NOTE: As individual market debt is not tracked here, the underlying markets
// need to be careful never to subtract more debt than they added.
// This can't be enforced without additional state/communication overhead.
totalDeposited = totalDeposited.sub(delta);
}
/* ---------- Market Lifecycle ---------- */
function createMarket(
bytes32 gameId,
string memory gameLabel,
uint maturity,
uint initialMint, // initial sUSD to mint options for,
uint positionCount,
uint[] memory tags,
bool isChild,
address parentMarket
)
external
override
notPaused
returns (
ISportPositionalMarket // no support for returning PositionalMarket polymorphically given the interface
)
{
require(marketCreationEnabled, "Market creation is disabled");
require(
msg.sender == theRundownConsumer || msg.sender == oddsObtainer || msg.sender == playerProps,
"Invalid creator"
);
uint expiry = maturity.add(expiryDuration);
require(block.timestamp < maturity, "Maturity has to be in the future");
// We also require maturity < expiry. But there is no need to check this.
// The market itself validates the capital and skew requirements.
ISportPositionalMarket market = _createMarket(
SportPositionalMarketFactory.SportPositionCreationMarketParameters(
msg.sender,
gameId,
gameLabel,
[maturity, expiry],
positionCount,
tags,
isChild,
parentMarket,
false
)
);
if (positionCount > 2 && isDoubleChanceSupported) {
_createDoubleChanceMarkets(msg.sender, gameId, maturity, expiry, address(market), tags[0]);
}
return market;
}
function createDoubleChanceMarketsForParent(address market) external notPaused onlyOwner {
require(marketCreationEnabled, "Market creation is disabled");
require(isDoubleChanceSupported, "Double chance not supported");
ISportPositionalMarket marketContract = ISportPositionalMarket(market);
require(marketContract.optionsCount() > 2, "Not supported for 2 options market");
(uint maturity, uint expiry) = marketContract.times();
_createDoubleChanceMarkets(
marketContract.creator(),
marketContract.getGameId(),
maturity,
expiry,
market,
marketContract.tags(0)
);
}
function _createMarket(SportPositionalMarketFactory.SportPositionCreationMarketParameters memory parameters)
internal
returns (ISportPositionalMarket)
{
SportPositionalMarket market = SportPositionalMarketFactory(sportPositionalMarketFactory).createMarket(parameters);
_activeMarkets.add(address(market));
(IPosition up, IPosition down, IPosition draw) = market.getOptions();
emit MarketCreated(
address(market),
parameters.creator,
parameters.gameId,
parameters.times[0],
parameters.times[1],
address(up),
address(down),
address(draw)
);
emit MarketLabel(address(market), parameters.gameLabel);
return market;
}
function _createDoubleChanceMarkets(
address creator,
bytes32 gameId,
uint maturity,
uint expiry,
address market,
uint tag
) internal onlySupportedGameId(gameId) {
string[3] memory labels = ["HomeTeamNotToLose", "AwayTeamNotToLose", "NoDraw"];
uint[] memory tagsDoubleChance = new uint[](2);
tagsDoubleChance[0] = tag;
tagsDoubleChance[1] = 10003;
for (uint i = 0; i < 3; i++) {
ISportPositionalMarket doubleChanceMarket = _createMarket(
SportPositionalMarketFactory.SportPositionCreationMarketParameters(
creator,
gameId,
labels[i],
[maturity, expiry],
2,
tagsDoubleChance,
false,
address(market),
true
)
);
_activeMarkets.add(address(doubleChanceMarket));
doubleChanceMarketsByParent[address(market)].push(address(doubleChanceMarket));
isDoubleChance[address(doubleChanceMarket)] = true;
IGamesOddsObtainer(oddsObtainer).setChildMarketGameId(gameId, address(doubleChanceMarket));
emit DoubleChanceMarketCreated(address(market), address(doubleChanceMarket), tagsDoubleChance[1], labels[i]);
}
}
function transferSusdTo(
address sender,
address receiver,
uint amount
) external override {
//only to be called by markets themselves
require(isKnownMarket(address(msg.sender)), "Market unknown.");
amount = _transformCollateral(amount);
amount = needsTransformingCollateral ? amount + 1 : amount;
bool success = sUSD.transferFrom(sender, receiver, amount);
if (!success) {
revert("TransferFrom function failed");
}
}
function resolveMarket(address market, uint _outcome) external override {
require(
msg.sender == theRundownConsumer ||
msg.sender == owner ||
msg.sender == oddsObtainer ||
msg.sender == playerProps ||
whitelistedCancelAddresses[msg.sender],
"Invalid resolver"
);
require(_activeMarkets.contains(market), "Not an active market");
require(!isDoubleChance[market], "Not supported for double chance markets");
// unpause if paused
if (ISportPositionalMarket(market).paused()) {
ISportPositionalMarket(market).setPaused(false);
}
SportPositionalMarket(market).resolve(_outcome);
_activeMarkets.remove(market);
_maturedMarkets.add(market);
if (doubleChanceMarketsByParent[market].length > 0) {
if (_outcome == 1) {
// HomeTeamNotLose, NoDraw
SportPositionalMarket(doubleChanceMarketsByParent[market][0]).resolve(1);
SportPositionalMarket(doubleChanceMarketsByParent[market][1]).resolve(2);
SportPositionalMarket(doubleChanceMarketsByParent[market][2]).resolve(1);
} else if (_outcome == 2) {
// AwayTeamNotLose, NoDraw
SportPositionalMarket(doubleChanceMarketsByParent[market][0]).resolve(2);
SportPositionalMarket(doubleChanceMarketsByParent[market][1]).resolve(1);
SportPositionalMarket(doubleChanceMarketsByParent[market][2]).resolve(1);
} else if (_outcome == 3) {
// HomeTeamNotLose, AwayTeamNotLose
SportPositionalMarket(doubleChanceMarketsByParent[market][0]).resolve(1);
SportPositionalMarket(doubleChanceMarketsByParent[market][1]).resolve(1);
SportPositionalMarket(doubleChanceMarketsByParent[market][2]).resolve(2);
} else {
// cancelled
SportPositionalMarket(doubleChanceMarketsByParent[market][0]).resolve(0);
SportPositionalMarket(doubleChanceMarketsByParent[market][1]).resolve(0);
SportPositionalMarket(doubleChanceMarketsByParent[market][2]).resolve(0);
}
for (uint i = 0; i < doubleChanceMarketsByParent[market].length; i++) {
_activeMarkets.remove(doubleChanceMarketsByParent[market][i]);
_maturedMarkets.add(doubleChanceMarketsByParent[market][i]);
}
}
}
function resolveMarketWithResult(
address _market,
uint _outcome,
uint8 _homeScore,
uint8 _awayScore,
address _consumer,
bool _useBackupOdds
) external {
require(msg.sender == owner || whitelistedCancelAddresses[msg.sender], "Invalid resolver");
require(!isDoubleChance[_market], "Not supported for double chance markets");
if (_outcome != 0) {
require(!_useBackupOdds, "Only use backup odds on cancelation, if needed!");
}
if (_consumer == theRundownConsumer) {
ITherundownConsumer(theRundownConsumer).resolveMarketManually(
_market,
_outcome,
_homeScore,
_awayScore,
_useBackupOdds
);
}
}
function cancelMarketsForParents(address[] memory _parents) external {
for (uint i = 0; i < _parents.length; i++) {
require(!isDoubleChance[_parents[i]], "Not supported for double chance markets");
(bool _hasAnyMints, uint _maturity) = _hasAnyMintsAndMaturityDatesForMarket(_parents[i]);
if (!_hasAnyMints && _maturity <= block.timestamp && !_isResolvedMarket(_parents[i])) {
ITherundownConsumer(theRundownConsumer).resolveMarketManually(_parents[i], 0, 0, 0, false);
}
}
}
function cancelMarketsForPlayerProps(address[] memory _playerPropsMarkets) external {
for (uint i = 0; i < _playerPropsMarkets.length; i++) {
require(!isDoubleChance[_playerPropsMarkets[i]], "Not supported for double chance markets");
(bool _hasAnyMints, uint _maturity) = _hasAnyMintsAndMaturityDatesForPP(_playerPropsMarkets[i]);
if (!_hasAnyMints && _maturity <= block.timestamp && !_isResolvedMarket(_playerPropsMarkets[i])) {
IGamesPlayerProps(playerProps).cancelMarketFromManager(_playerPropsMarkets[i]);
}
}
}
function overrideResolveWithCancel(address market, uint _outcome) external {
require(msg.sender == owner || whitelistedCancelAddresses[msg.sender], "Invalid resolver");
require(_outcome == 0, "Can only set 0 outcome");
require(SportPositionalMarket(market).resolved(), "Market not resolved");
require(!_activeMarkets.contains(market), "Active market");
require(!isDoubleChance[market], "Not supported for double chance markets");
// unpause if paused
if (ISportPositionalMarket(market).paused()) {
ISportPositionalMarket(market).setPaused(false);
}
SportPositionalMarket(market).resolve(_outcome);
if (doubleChanceMarketsByParent[market].length > 0) {
SportPositionalMarket(doubleChanceMarketsByParent[market][0]).resolve(0);
SportPositionalMarket(doubleChanceMarketsByParent[market][1]).resolve(0);
SportPositionalMarket(doubleChanceMarketsByParent[market][2]).resolve(0);
}
}
function expireMarkets(address[] calldata markets) external override notPaused onlyOwner {
for (uint i = 0; i < markets.length; i++) {
address market = markets[i];
require(isKnownMarket(address(market)), "Market unknown.");
// The market itself handles decrementing the total deposits.
SportPositionalMarket(market).expire(payable(msg.sender));
// Note that we required that the market is known, which guarantees
// its index is defined and that the list of markets is not empty.
_maturedMarkets.remove(market);
emit MarketExpired(market);
}
}
function restoreInvalidOddsForMarket(
address _market,
uint _homeOdds,
uint _awayOdds,
uint _drawOdds
) external onlyOwner {
require(isKnownMarket(address(_market)), "Market unknown.");
require(SportPositionalMarket(_market).cancelled(), "Market not cancelled.");
SportPositionalMarket(_market).restoreInvalidOdds(_homeOdds, _awayOdds, _drawOdds);
emit OddsForMarketRestored(_market, _homeOdds, _awayOdds, _drawOdds);
}
function setMarketCreationEnabled(bool enabled) external onlyOwner {
if (enabled != marketCreationEnabled) {
marketCreationEnabled = enabled;
emit MarketCreationEnabledUpdated(enabled);
}
}
function setCancelTimeout(uint _cancelTimeout) external onlyOwner {
cancelTimeout = _cancelTimeout;
}
function setIsDoubleChanceSupported(bool _isDoubleChanceSupported) external onlyOwner {
isDoubleChanceSupported = _isDoubleChanceSupported;
emit DoubleChanceSupportChanged(_isDoubleChanceSupported);
}
function setSupportedSportForDoubleChance(uint[] memory _sportIds, bool _isSupported) external onlyOwner {
for (uint256 index = 0; index < _sportIds.length; index++) {
// only if current flag is different, if same skip it
if (doesSportSupportDoubleChance[_sportIds[index]] != _isSupported) {
doesSportSupportDoubleChance[_sportIds[index]] = _isSupported;
emit SupportedSportForDoubleChanceAdded(_sportIds[index], _isSupported);
}
}
}
// support USDC with 6 decimals
function transformCollateral(uint value) external view override returns (uint) {
return _transformCollateral(value);
}
function _transformCollateral(uint value) internal view returns (uint) {
if (needsTransformingCollateral) {
return value / 1e12;
} else {
return value;
}
}
function _updateDatesForMarket(
address _market,
uint256 _newStartTime,
uint256 _expiry
) internal {
ISportPositionalMarket(_market).updateDates(_newStartTime, _expiry);
emit DatesUpdatedForMarket(_market, _newStartTime, _expiry);
}
function _hasAnyMintsForChildren(address _market) internal view returns (bool) {
return _hasAnyMints(_market, true) || _hasAnyMints(_market, false) || _hasAnyMintsDoubleChance(_market);
}
function _hasAnyMints(address _market, bool checkPlayerProps) internal view returns (bool) {
uint numberOfChildMarkets;
address childMarketContract;
if (checkPlayerProps) {
numberOfChildMarkets = IGameChildMarket(playerProps).numberOfChildMarkets(_market);
childMarketContract = playerProps;
} else {
numberOfChildMarkets = IGameChildMarket(oddsObtainer).numberOfChildMarkets(_market);
childMarketContract = oddsObtainer;
}
for (uint i = 0; i < numberOfChildMarkets; i++) {
address child = IGameChildMarket(childMarketContract).mainMarketChildMarketIndex(_market, i);
if (ISportPositionalMarket(child).optionsInitialized()) {
return true;
}
}
return false;
}
function _hasAnyMintsDoubleChance(address _market) internal view returns (bool) {
address[] memory childMarkets = _getDoubleChanceMarkets(_market);
for (uint i = 0; i < childMarkets.length; i++) {
if (childMarkets[i] != address(0) && ISportPositionalMarket(childMarkets[i]).optionsInitialized()) {
return true;
}
}
return false;
}
function reverseTransformCollateral(uint value) external view override returns (uint) {
if (needsTransformingCollateral) {
return value * 1e12;
} else {
return value;
}
}
function isWhitelistedAddress(address _address) external view override returns (bool) {
return whitelistedAddresses[_address];
}
/* ========== MODIFIERS ========== */
modifier onlyActiveMarkets() {
require(_activeMarkets.contains(msg.sender), "Permitted only for active markets.");
_;
}
modifier onlyKnownMarkets() {
require(isKnownMarket(msg.sender), "Permitted only for known markets.");
_;
}
modifier onlySupportedGameId(bytes32 gameId) {
uint sportId = ITherundownConsumer(theRundownConsumer).sportsIdPerGame(gameId);
if (doesSportSupportDoubleChance[sportId] && isDoubleChanceSupported) {
_;
}
}
/* ========== EVENTS ========== */
event MarketCreated(
address market,
address indexed creator,
bytes32 indexed gameId,
uint maturityDate,
uint expiryDate,
address up,
address down,
address draw
);
event MarketLabel(address market, string gameLabel);
event MarketExpired(address market);
event MarketCreationEnabledUpdated(bool enabled);
event MarketsMigrated(SportPositionalMarketManager receivingManager, SportPositionalMarket[] markets);
event MarketsReceived(SportPositionalMarketManager migratingManager, SportPositionalMarket[] markets);
event SetMigratingManager(address migratingManager);
event ExpiryDurationUpdated(uint duration);
event MaxTimeToMaturityUpdated(uint duration);
event CreatorCapitalRequirementUpdated(uint value);
event SetSportPositionalMarketFactory(address _sportPositionalMarketFactory);
event SetsUSD(address _address);
event SetTherundownConsumer(address theRundownConsumer);
event SetObtainerAddress(address _obratiner);
event SetPlayerPropsAddress(address _playerProps);
event OddsForMarketRestored(address _market, uint _homeOdds, uint _awayOdds, uint _drawOdds);
event AddedIntoWhitelist(address _whitelistAddress, bool _flag);
event DatesUpdatedForMarket(address _market, uint256 _newStartTime, uint256 _expiry);
event DoubleChanceMarketCreated(address _parentMarket, address _doubleChanceMarket, uint tag, string label);
event DoubleChanceSupportChanged(bool _isDoubleChanceSupported);
event SupportedSportForDoubleChanceAdded(uint _sportId, bool _isSupported);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Inheritance
import "./ProxyOwned.sol";
// Clone of syntetix contract without constructor
contract ProxyPausable is ProxyOwned {
uint public lastPauseTime;
bool public paused;
/**
* @notice Change the paused state of the contract
* @dev Only the contract owner may call this.
*/
function setPaused(bool _paused) external onlyOwner {
// Ensure we're actually changing the state before we do anything
if (_paused == paused) {
return;
}
// Set our paused state.
paused = _paused;
// If applicable, set the last pause time.
if (paused) {
lastPauseTime = block.timestamp;
}
// Let everyone know that our pause state has changed.
emit PauseChanged(paused);
}
event PauseChanged(bool isPaused);
modifier notPaused {
require(!paused, "This action cannot be performed while the contract is paused");
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library AddressSetLib {
struct AddressSet {
address[] elements;
mapping(address => uint) indices;
}
function contains(AddressSet storage set, address candidate) internal view returns (bool) {
if (set.elements.length == 0) {
return false;
}
uint index = set.indices[candidate];
return index != 0 || set.elements[0] == candidate;
}
function getPage(
AddressSet storage set,
uint index,
uint pageSize
) internal view returns (address[] memory) {
// NOTE: This implementation should be converted to slice operators if the compiler is updated to v0.6.0+
uint endIndex = index + pageSize; // The check below that endIndex <= index handles overflow.
// If the page extends past the end of the list, truncate it.
if (endIndex > set.elements.length) {
endIndex = set.elements.length;
}
if (endIndex <= index) {
return new address[](0);
}
uint n = endIndex - index; // We already checked for negative overflow.
address[] memory page = new address[](n);
for (uint i; i < n; i++) {
page[i] = set.elements[i + index];
}
return page;
}
function add(AddressSet storage set, address element) internal {
// Adding to a set is an idempotent operation.
if (!contains(set, element)) {
set.indices[element] = set.elements.length;
set.elements.push(element);
}
}
function remove(AddressSet storage set, address element) internal {
require(contains(set, element), "Element not in set.");
// Replace the removed element with the last element of the list.
uint index = set.indices[element];
uint lastIndex = set.elements.length - 1; // We required that element is in the list, so it is not empty.
if (index != lastIndex) {
// No need to shift the last element if it is the one we want to delete.
address shiftedElement = set.elements[lastIndex];
set.elements[index] = shiftedElement;
set.indices[shiftedElement] = index;
}
set.elements.pop();
delete set.indices[element];
}
}pragma solidity ^0.8.0;
// Inheritance
import "../../utils/proxy/solidity-0.8.0/ProxyOwned.sol";
// Internal references
import "./SportPosition.sol";
import "./SportPositionalMarket.sol";
import "./SportPositionalMarketFactory.sol";
import "@openzeppelin/contracts-4.4.1/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-4.4.1/proxy/Clones.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract SportPositionalMarketFactory is Initializable, ProxyOwned {
/* ========== STATE VARIABLES ========== */
address public positionalMarketManager;
address public positionalMarketMastercopy;
address public positionMastercopy;
address public sportsAMM;
struct SportPositionCreationMarketParameters {
address creator;
bytes32 gameId;
string gameLabel;
uint[2] times; // [maturity, expiry]
uint positionCount;
uint[] tags;
bool isChild;
address parentMarket;
bool isDoubleChance;
}
/* ========== INITIALIZER ========== */
function initialize(address _owner) external initializer {
setOwner(_owner);
}
/* ========== MUTATIVE FUNCTIONS ========== */
function createMarket(SportPositionCreationMarketParameters calldata _parameters)
external
returns (SportPositionalMarket)
{
require(positionalMarketManager == msg.sender, "Only permitted by the manager.");
SportPositionalMarket pom = SportPositionalMarket(Clones.clone(positionalMarketMastercopy));
address[] memory positions = new address[](_parameters.positionCount);
pom.initialize(
SportPositionalMarket.SportPositionalMarketParameters(
positionalMarketManager,
_parameters.creator,
_parameters.gameId,
_parameters.gameLabel,
_parameters.times,
_parameters.positionCount,
positions,
_parameters.tags,
_parameters.isChild,
_parameters.parentMarket,
_parameters.isDoubleChance,
address(this)
)
);
emit MarketCreated(
address(pom),
_parameters.gameId,
_parameters.gameLabel,
_parameters.times[0],
_parameters.times[1],
0,
_parameters.positionCount,
_parameters.tags,
_parameters.isChild,
_parameters.parentMarket
);
return pom;
}
/* ========== SETTERS ========== */
function setSportPositionalMarketManager(address _positionalMarketManager) external onlyOwner {
positionalMarketManager = _positionalMarketManager;
emit SportPositionalMarketManagerChanged(_positionalMarketManager);
}
function setSportPositionalMarketMastercopy(address _positionalMarketMastercopy) external onlyOwner {
positionalMarketMastercopy = _positionalMarketMastercopy;
emit SportPositionalMarketMastercopyChanged(_positionalMarketMastercopy);
}
function setSportPositionMastercopy(address _positionMastercopy) external onlyOwner {
positionMastercopy = _positionMastercopy;
emit SportPositionMastercopyChanged(_positionMastercopy);
}
function setSportsAMM(address _sportsAMM) external onlyOwner {
sportsAMM = _sportsAMM;
emit SetSportsAMM(_sportsAMM);
}
event SportPositionalMarketManagerChanged(address _positionalMarketManager);
event SportPositionalMarketMastercopyChanged(address _positionalMarketMastercopy);
event SportPositionMastercopyChanged(address _positionMastercopy);
event SetSportsAMM(address _sportsAMM);
event SetLimitOrderProvider(address _limitOrderProvider);
event MarketCreated(
address market,
bytes32 indexed gameId,
string gameLabel,
uint maturityDate,
uint expiryDate,
uint initialMint,
uint positionCount,
uint[] tags,
bool isChild,
address parent
);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IGamesOddsObtainer {
struct GameOdds {
bytes32 gameId;
int24 homeOdds;
int24 awayOdds;
int24 drawOdds;
int16 spreadHome;
int24 spreadHomeOdds;
int16 spreadAway;
int24 spreadAwayOdds;
uint24 totalOver;
int24 totalOverOdds;
uint24 totalUnder;
int24 totalUnderOdds;
}
// view
function getActiveChildMarketsFromParent(address _parent) external view returns (address, address);
function getSpreadTotalsChildMarketsFromParent(address _parent)
external
view
returns (
uint numOfSpreadMarkets,
address[] memory spreadMarkets,
uint numOfTotalsMarkets,
address[] memory totalMarkets
);
function areOddsValid(
bytes32 _gameId,
bool _useBackup,
bool _isTwoPositional
) external view returns (bool);
function invalidOdds(address _market) external view returns (bool);
function playersReportTimestamp(address _market) external view returns (uint);
function getNormalizedOdds(bytes32 _gameId) external view returns (uint[] memory);
function getNormalizedOddsForMarket(address _market) external view returns (uint[] memory);
function getOddsForGames(bytes32[] memory _gameIds) external view returns (int24[] memory odds);
function mainMarketChildMarketIndex(address _main, uint _index) external view returns (address);
function numberOfChildMarkets(address _main) external view returns (uint);
function mainMarketSpreadChildMarket(address _main, int16 _spread) external view returns (address);
function mainMarketTotalChildMarket(address _main, uint24 _total) external view returns (address);
function childMarketMainMarket(address _market) external view returns (address);
function childMarketTotal(address _market) external view returns (uint24);
function currentActiveTotalChildMarket(address _main) external view returns (address);
function currentActiveSpreadChildMarket(address _main) external view returns (address);
function isSpreadChildMarket(address _child) external view returns (bool);
function childMarketCreated(address _child) external view returns (bool);
function getOddsForGame(bytes32 _gameId)
external
view
returns (
int24,
int24,
int24,
int24,
int24,
int24,
int24
);
function getLinesForGame(bytes32 _gameId)
external
view
returns (
int16,
int16,
uint24,
uint24
);
// executable
function obtainOdds(
bytes32 requestId,
GameOdds memory _game,
uint _sportId,
address _main,
bool _isTwoPositional,
bool _isPlayersReport
) external;
function setFirstOdds(
bytes32 _gameId,
int24 _homeOdds,
int24 _awayOdds,
int24 _drawOdds
) external;
function setFirstNormalizedOdds(
bytes32 _gameId,
address _market,
bool _isTwoPositional
) external;
function setBackupOddsAsMainOddsForGame(bytes32 _gameId) external;
function pauseUnpauseChildMarkets(address _main, bool _flag) external;
function pauseUnpauseCurrentActiveChildMarket(
bytes32 _gameId,
address _main,
bool _flag
) external;
function resolveChildMarkets(
address _market,
uint _outcome,
uint8 _homeScore,
uint8 _awayScore
) external;
function setChildMarketGameId(bytes32 gameId, address market) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IGamesPlayerProps {
struct PlayerProps {
bytes32 gameId;
bytes32 playerId;
uint8 option;
string playerName;
uint16 line;
int24 overOdds;
int24 underOdds;
}
struct PlayerPropsResolver {
bytes32 gameId;
bytes32 playerId;
uint8 option;
uint16 score;
uint8 statusId;
}
function obtainPlayerProps(PlayerProps memory _player, uint _sportId) external;
function cancelMartketsForPlayerInAGame(bytes32 gameId, bytes32 playerId) external;
function resolvePlayerProps(PlayerPropsResolver memory _result) external;
function cancelMarketFromManager(address _market) external;
function pauseAllPlayerPropsMarketForMain(
address _main,
bool _flag,
bool _invalidOddsOnMain,
bool _circuitBreakerMain
) external;
function createFulfilledForPlayerProps(
bytes32 gameId,
bytes32 playerId,
uint8 option
) external view returns (bool);
function cancelPlayerPropsMarketForMain(address _main) external;
function getNormalizedOddsForMarket(address _market) external view returns (uint[] memory);
function mainMarketChildMarketIndex(address _main, uint _index) external view returns (address);
function numberOfChildMarkets(address _main) external view returns (uint);
function doesSportSupportPlayerProps(uint _sportId) external view returns (bool);
function pausedByInvalidOddsOnMain(address _main) external view returns (bool);
function pausedByCircuitBreakerOnMain(address _main) external view returns (bool);
function playerIdPerChildMarket(address _market) external view returns (bytes32);
function optionIdPerChildMarket(address _market) external view returns (uint8);
function getAllOptionsWithPlayersForGameId(bytes32 _gameId)
external
view
returns (
bytes32[] memory _playerIds,
uint8[] memory _options,
bool[] memory _isResolved,
address[][] memory _childMarketsPerOption
);
function getPlayerPropsDataForMarket(address _market)
external
view
returns (
address,
bytes32,
bytes32,
uint8
);
function getPlayerPropForOption(
bytes32 gameId,
bytes32 playerId,
uint8 option
)
external
view
returns (
uint16,
int24,
int24,
bool
);
function fulfillPlayerPropsCLResolved(bytes[] memory _playerProps) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IGameChildMarket {
function mainMarketChildMarketIndex(address _main, uint _index) external view returns (address);
function numberOfChildMarkets(address _main) external view returns (uint);
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_whitelistAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"_flag","type":"bool"}],"name":"AddedIntoWhitelist","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_whitelistAddress","type":"address"},{"indexed":false,"internalType":"bool","name":"_flag","type":"bool"}],"name":"AddedIntoWhitelistStaker","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newProvider","type":"address"}],"name":"DefaultLiquidityProviderChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"LeftoverFundsPulled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxAllowedDeposit","type":"uint256"}],"name":"MaxAllowedDepositChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"MaxAllowedUsersChanged","type":"uint256"}],"name":"MaxAllowedUsersChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minAllowedDeposit","type":"uint256"}],"name":"MinAllowedDepositChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","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":"newMastercopy","type":"address"}],"name":"PoolRoundMastercopyChanged","type":"event"},{"anonymous":false,"inputs":[],"name":"PoolStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"roundPnL","type":"uint256"}],"name":"RoundClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"batchSize","type":"uint256"}],"name":"RoundClosingBatchProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"round","type":"uint256"}],"name":"RoundClosingPrepared","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"roundLength","type":"uint256"}],"name":"RoundLengthChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_round","type":"uint256"},{"indexed":false,"internalType":"address","name":"roundPool","type":"address"}],"name":"RoundPoolCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"safeBoxShare","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"safeBoxAmount","type":"uint256"}],"name":"SafeBoxSharePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"safeBox","type":"address"},{"indexed":false,"internalType":"uint256","name":"safeBoxImpact","type":"uint256"}],"name":"SetSafeBoxParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sportAMM","type":"address"}],"name":"SportAMMChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_stakedThalesMultiplier","type":"uint256"}],"name":"StakedThalesMultiplierChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"stakingThales","type":"address"}],"name":"StakingThalesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"utilizationRate","type":"uint256"}],"name":"UtilizationRateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"WithdrawalRequested","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allocationPerRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"balancesPerRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canCloseCurrentRound","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"closeRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"amountToMint","type":"uint256"}],"name":"commitTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"roundA","type":"uint256"},{"internalType":"uint256","name":"roundB","type":"uint256"}],"name":"cumulativePnLBetweenRounds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"cumulativeProfitAndLoss","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultLiquidityProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exerciseMarketsReadyToExercised","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"batchSize","type":"uint256"}],"name":"exerciseMarketsReadyToExercisedBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"firstRoundStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"getMarketPool","outputs":[{"internalType":"address","name":"roundPool","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"getMarketRound","outputs":[{"internalType":"uint256","name":"_round","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getMaxAvailableDepositForUser","outputs":[{"internalType":"uint256","name":"maxDepositForUser","type":"uint256"},{"internalType":"uint256","name":"availableToDepositForUser","type":"uint256"},{"internalType":"uint256","name":"stakedThalesForUser","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"getOrCreateMarketPool","outputs":[{"internalType":"address","name":"roundPool","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_round","type":"uint256"}],"name":"getRoundEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_round","type":"uint256"}],"name":"getRoundStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_round","type":"uint256"}],"name":"getTradingMarketsPerRound","outputs":[{"internalType":"uint256","name":"numOfMarkets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsersCountInCurrentRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasMarketsReadyToBeExercised","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initNonReentrant","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_parlayAMM","type":"address"},{"internalType":"contract IERC20Upgradeable","name":"_sUSD","type":"address"},{"internalType":"uint256","name":"_roundLength","type":"uint256"},{"internalType":"uint256","name":"_maxAllowedDeposit","type":"uint256"},{"internalType":"uint256","name":"_minDepositAmount","type":"uint256"},{"internalType":"uint256","name":"_maxAllowedUsers","type":"uint256"},{"internalType":"bool","name":"_needsTransformingCollateral","type":"bool"}],"internalType":"struct ParlayAMMLiquidityPool.InitParams","name":"params","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"isTradingMarketInARound","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isUserLPing","outputs":[{"internalType":"bool","name":"isUserInLP","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"marketAlreadyExercisedInRound","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAllowedDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAllowedUsers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDepositAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"needsTransformingCollateral","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onlyWhitelistedStakersAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"parlayAMM","outputs":[{"internalType":"contract IParlayMarketsAMM","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"parlayMarketRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"share","type":"uint256"}],"name":"partialWithdrawalRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolRoundMastercopy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prepareRoundClosing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"batchSize","type":"uint256"}],"name":"processRoundClosingBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"profitAndLossPerRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"rounds","type":"uint256[]"}],"name":"retrieveLeftoverRoundFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"round","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundClosingPrepared","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"roundPools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sUSD","outputs":[{"internalType":"contract IERC20Upgradeable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"safeBox","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"safeBoxImpact","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_defaultLiquidityProvider","type":"address"}],"name":"setDefaultLiquidityProvider","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxAllowedDeposit","type":"uint256"}],"name":"setMaxAllowedDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxAllowedUsers","type":"uint256"}],"name":"setMaxAllowedUsers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minDepositAmount","type":"uint256"}],"name":"setMinAllowedDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IParlayMarketsAMM","name":"_parlayAMM","type":"address"}],"name":"setParlayAmm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_setPausing","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_poolRoundMastercopy","type":"address"}],"name":"setPoolRoundMastercopy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_roundLength","type":"uint256"}],"name":"setRoundLength","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_safeBox","type":"address"},{"internalType":"uint256","name":"_safeBoxImpact","type":"uint256"}],"name":"setSafeBoxParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IStakingThales","name":"_stakingThales","type":"address"}],"name":"setStakingThales","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_utilizationRate","type":"uint256"}],"name":"setUtilizationRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakedThalesMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingThales","outputs":[{"internalType":"contract IStakingThales","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"start","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"started","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDeposited","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"tradingMarketsPerRound","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAddress","type":"address"}],"name":"transferOwnershipAtInit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_market","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"transferToPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"userInRound","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usersCurrentlyInPool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"usersPerRound","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usersProcessedInRound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"utilizationRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedDeposits","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedStakers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawalRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawalRequested","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawalShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b506157d780620000216000396000f3fe608060405234801561001057600080fd5b506004361061048b5760003560e01c80637a1e0aa811610262578063be9a655511610151578063ddcc8fe9116100ce578063efcac68911610092578063efcac68914610a7e578063f475f13b14610a91578063f61fcb8b14610aa4578063fd8a8cc614610ac4578063fdaf17f014610ad7578063ff50abdc14610afa57600080fd5b8063ddcc8fe914610a4b578063e278fe6f14610a5e578063e3041fd914610a66578063ebc7977214610a6e578063ee161cce14610a7657600080fd5b8063d69fb66811610115578063d69fb668146109f6578063d7efa129146109ff578063d95ad45c14610a12578063db7f92d414610a25578063ddc6ac2314610a3857600080fd5b8063be9a6555146109ac578063c2edfc73146109b4578063c3b83f5f146109c7578063c9f4ff46146109da578063d27c0797146109ed57600080fd5b80639324cac7116101df578063b6b55f25116101a3578063b6b55f2514610962578063b745abe314610975578063b9b1be8b1461097d578063bcfa893714610990578063bdcc22e9146109a357600080fd5b80639324cac7146108fc5780639bd2e61b1461090f5780639faf680214610922578063a8df539f14610935578063b01862151461094257600080fd5b80638b649b94116102265780638b649b94146108bc5780638b844412146108c55780638da5cb5b146108cd5780638fe812b4146108e6578063930102f2146108f357600080fd5b80637a1e0aa8146108485780637d550e051461085b5780637f7b8c50146108735780637f8525821461089657806384c095c2146108a957600080fd5b806348663e951161037e578063634e0d97116102fb5780636c321c8a116102bf5780636c321c8a146107d657806371143ab9146107df5780637261b81a1461080d57806374094edd1461082057806379ba50971461084057600080fd5b8063634e0d9714610762578063645006ca1461079057806365e0e725146107995780636685fdc2146107ac578063681312f5146107c357600080fd5b806358c09cc01161034257806358c09cc0146107075780635c7b396e1461071a5780635c975abb146107235780635ddd3e831461072e578063610589e11461075957600080fd5b806348663e951461068e5780634ae7937f146106a15780634b141b3f146106c15780634d549a42146106e157806353a47bb7146106f457600080fd5b80631baa88561161040c578063336d30ed116103d0578063336d30ed1461060c578063343e4f9f1461062c5780633b92d7581461063f57806340774ff6146106525780634651f0801461066557600080fd5b80631baa8856146105b15780631daae173146105ba5780631f2698ab146105dd578063202ffce8146105f1578063311c56df1461060457600080fd5b8063146ca53111610453578063146ca531146105415780631627540c1461054a57806316c38b3c1461055d578063175e6700146105705780631b2a52d81461059e57600080fd5b806302f97b0c14610490578063042047cf146104d3578063082f9fd4146104e057806312b19a131461050b57806313af40351461052c575b600080fd5b6104be61049e3660046153ae565b608360209081526000928352604080842090915290825290205460ff1681565b60405190151581526020015b60405180910390f35b6080546104be9060ff1681565b6104f36104ee3660046153dd565b610b03565b6040516001600160a01b0390911681526020016104ca565b61051e61051936600461537e565b610b3b565b6040519081526020016104ca565b61053f61053a36600461510d565b610b69565b005b61051e60695481565b61053f61055836600461510d565b610ca9565b61053f61056b366004615256565b610cff565b61058361057e36600461510d565b610d1f565b604080519384526020840192909252908201526060016104ca565b61053f6105ac36600461537e565b610e89565b61051e606b5481565b6104be6105c836600461510d565b60716020526000908152604090205460ff1681565b6068546104be90600160a01b900460ff1681565b61053f6105ff36600461537e565b6114d7565b61053f611514565b61051e61061a36600461537e565b60756020526000908152604090205481565b6104f361063a3660046153dd565b611793565b607a546104f3906001600160a01b031681565b61053f61066036600461537e565b6117af565b6104f361067336600461537e565b606c602052600090815260409020546001600160a01b031681565b6089546104f3906001600160a01b031681565b61051e6106af36600461537e565b60706020526000908152604090205481565b61051e6106cf36600461537e565b60009081526072602052604090205490565b61053f6106ef36600461510d565b6117ec565b6001546104f3906001600160a01b031681565b61053f610715366004615129565b611868565b61051e60855481565b60345460ff166104be565b61051e61073c3660046153ae565b606f60209081526000928352604080842090915290825290205481565b61051e60785481565b6104be6107703660046153ae565b606e60209081526000928352604080842090915290825290205460ff1681565b61051e60775481565b61053f6107a736600461510d565b611cfa565b6069546000908152606d602052604090205461051e565b61053f6107d136600461537e565b611d76565b61051e60885481565b6104be6107ed3660046153ae565b607360209081526000928352604080842090915290825290205460ff1681565b61053f61081b36600461536c565b611e1b565b61051e61082e36600461537e565b60746020526000908152604090205481565b61053f61203d565b61053f610856366004615129565b61213a565b6067546104f39061010090046001600160a01b031681565b6104be61088136600461510d565b607e6020526000908152604090205460ff1681565b61053f6108a436600461510d565b6121a2565b61053f6108b736600461510d565b61221e565b61051e606a5481565b61053f61232e565b6000546104f3906201000090046001600160a01b031681565b6082546104be9060ff1681565b61051e607c5481565b6068546104f3906001600160a01b031681565b61053f61091d36600461537e565b612625565b61051e61093036600461510d565b61292b565b6084546104be9060ff1681565b61051e61095036600461510d565b60876020526000908152604090205481565b61053f61097036600461537e565b612b62565b6104be613028565b607d546104f3906001600160a01b031681565b61053f61099e36600461537e565b6131d9565b61051e60795481565b61053f6135d5565b6104f36109c236600461510d565b613784565b61053f6109d536600461510d565b6137b3565b61051e6109e83660046153dd565b6138cc565b61051e60765481565b61051e608a5481565b61053f610a0d366004615129565b61390c565b6104be610a2036600461510d565b613a39565b61053f610a3336600461537e565b613af8565b61051e610a4636600461537e565b613b35565b61053f610a5936600461537e565b613b46565b61053f613b83565b61053f61400f565b61053f61436b565b6104be6143c9565b61053f610a8c3660046151e6565b614520565b6104f3610a9f36600461510d565b61476d565b61051e610ab236600461510d565b60866020526000908152604090205481565b607b546104f3906001600160a01b031681565b6104be610ae536600461510d565b60816020526000908152604090205460ff1681565b61051e607f5481565b60726020528160005260406000208181548110610b1f57600080fd5b6000918252602090912001546001600160a01b03169150829050565b606a54600090610b4c6001846156f4565b610b5691906156d5565b606b54610b63919061569d565b92915050565b6001600160a01b038116610bc45760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f7420626520300000000000000060448201526064015b60405180910390fd5b600154600160a01b900460ff1615610c305760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610bbb565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b03831662010000810262010000600160b01b03199092169190911782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91015b60405180910390a150565b610cb1614840565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290602001610c9e565b610d07614840565b80610d1757610d146148ba565b50565b610d1461494d565b6000806000806069546001610d34919061569d565b607b54604051631676539160e01b81526001600160a01b03888116600483015292935091169063167653919060240160206040518083038186803b158015610d7b57600080fd5b505afa158015610d8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610db39190615396565b9150610dde670de0b6b3a7640000607c5484610dcf91906156d5565b610dd991906156b5565b6149a5565b6000828152606f602081815260408084206001600160a01b038b1680865290835281852054606954865293835281852090855290915290912054919550610e249161569d565b8411610e31576000610e7f565b6000818152606f602081815260408084206001600160a01b038a1680865290835281852054606954865293835281852090855290915290912054610e7590866156f4565b610e7f91906156f4565b9250509193909250565b600160666000828254610e9c919061569d565b909155505060665460345460ff1615610ec75760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16610f195760405162461bcd60e51b815260206004820152601a60248201527f526f756e6420636c6f73696e67206e6f742070726570617265640000000000006044820152606401610bbb565b6069546000908152606d602052604090205460855410610f7b5760405162461bcd60e51b815260206004820152601b60248201527f416c6c20757365727320616c72656164792070726f63657373656400000000006044820152606401610bbb565b60008211610f9b5760405162461bcd60e51b8152600401610bbb906155ab565b6069546000908152606c60205260408120546085546001600160a01b039091169190610fc890859061569d565b6069546000908152606d6020526040902054909150811115610ff857506069546000908152606d60205260409020545b6085545b81811015611473576069546000908152606d6020526040812080548390811061103557634e487b7160e01b600052603260045260246000fd5b6000918252602080832090910154606954835260748252604080842054606f84528185206001600160a01b0390931680865292909352832054909350670de0b6b3a764000091611084916156d5565b61108e91906156b5565b6001600160a01b03831660009081526071602052604090205490915060ff161580156110ca575060695460009081526074602052604090205415155b1561122f5780606f600060695460016110e3919061569d565b81526020019081526020016000206000846001600160a01b03166001600160a01b031681526020019081526020016000205461111f919061569d565b606f60006069546001611132919061569d565b81526020019081526020016000206000846001600160a01b03166001600160a01b0316815260200190815260200160002081905550606d6000606954600161117a919061569d565b8152602080820192909252604001600090812080546001810182559082529190200180546001600160a01b0319166001600160a01b0384811691909117909155607b54161561122a57607b546040516302c7739b60e01b81526001600160a01b03848116600483015260248201849052909116906302c7739b90604401600060405180830381600087803b15801561121157600080fd5b505af1158015611225573d6000803e3d6000fd5b505050505b61144d565b6001600160a01b038216600090815260866020526040902054156113a5576001600160a01b038216600090815260866020526040812054670de0b6b3a76400009061127a90846156d5565b61128491906156b5565b60685490915061129f906001600160a01b03168785846149c6565b604080516001600160a01b0385168152602081018390527fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a910160405180910390a16001600160a01b0383166000908152607160209081526040808320805460ff1916905560869091528120819055606954606d919061132090600161569d565b8152602080820192909252604001600090812080546001810182559082529190200180546001600160a01b0319166001600160a01b03851617905561136581836156f4565b606f60006069546001611378919061569d565b8152602080820192909252604090810160009081206001600160a01b03881682529092529020555061144d565b6000606f600060695460016113ba919061569d565b8152602080820192909252604090810160009081206001600160a01b038088168352935220919091556068546113f391168684846149c6565b6001600160a01b038216600081815260716020908152604091829020805460ff19169055815192835282018390527fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a910160405180910390a15b60855461145b90600161569d565b6085555081905061146b81615737565b915050610ffc565b5060695460408051918252602082018690527f2e692c8fcabe33ba22535323e79dcb54ef22dccdb8e4ebdd9f2a7ffb1a28856c910160405180910390a1505060665481146114d35760405162461bcd60e51b8152600401610bbb906155ed565b5050565b6114df614840565b60788190556040518181527fe7c2c09f66c8b970b4a99250f4d0844e1496b9d51d4760a17b0134ddd52023e190602001610c9e565b600160666000828254611527919061569d565b9091555050606654606854600160a01b900460ff166115585760405162461bcd60e51b8152600401610bbb90615470565b3360009081526071602052604090205460ff16156115b85760405162461bcd60e51b815260206004820152601c60248201527f5769746864726177616c20616c726561647920726571756573746564000000006044820152606401610bbb565b6069546000908152606f602090815260408083203384529091529020546116175760405162461bcd60e51b81526020600482015260136024820152724e6f7468696e6720746f20776974686472617760681b6044820152606401610bbb565b606f6000606954600161162a919061569d565b815260208082019290925260409081016000908120338252909252902054156116655760405162461bcd60e51b8152600401610bbb90615555565b60345460ff16156116885760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16156116ab5760405162461bcd60e51b8152600401610bbb9061550e565b6069546000908152606f60209081526040808320338452909152902054607f54111561170c576069546000908152606f60209081526040808320338452909152812054607f8054919290916117019084906156f4565b909155506117129050565b6000607f555b600160795461172191906156f4565b60795533600081815260716020908152604091829020805460ff1916600117905590519182527fe5892ff2a8b08efb903ffbba1f0514c1d3e22eea34dd5b89cf30aabce03dde5a910160405180910390a16066548114610d145760405162461bcd60e51b8152600401610bbb906155ed565b606d6020528160005260406000208181548110610b1f57600080fd5b6117b7614840565b60888190556040518181527fc117ccf765672707ebe3c1606037488c1e27dc1f42b5266e3d6b496db7d4209e90602001610c9e565b6117f4614840565b6001600160a01b03811661181a5760405162461bcd60e51b8152600401610bbb90615666565b607d80546001600160a01b0319166001600160a01b0383169081179091556040519081527fa65a5aa86bb6f8e75752296da3ebda45474c8a302fc640c3b62868793862a03390602001610c9e565b60016066600082825461187b919061569d565b909155505060665460345460ff16156118a65760405162461bcd60e51b8152600401610bbb906154e4565b60675461010090046001600160a01b031633146118d55760405162461bcd60e51b8152600401610bbb9061549e565b60845460ff16156118f85760405162461bcd60e51b8152600401610bbb9061550e565b606854600160a01b900460ff166119215760405162461bcd60e51b8152600401610bbb90615470565b600082116119715760405162461bcd60e51b815260206004820152601960248201527f43616e277420636f6d6d69742061207a65726f207472616465000000000000006044820152606401610bbb565b61197a826149a5565b60825490925060ff1661198d5781611998565b61199882600161569d565b915060006119a58461292b565b6001600160a01b03851660009081526087602052604081208290559091506119cc82614a20565b9050606954821415611b31576067546068546119fc916001600160a01b03918216918491610100900416876149c6565b608854606954600090815260706020526040902054670de0b6b3a764000091611a24916156d5565b611a2e91906156b5565b606954600090815260706020526040902054611a4a91906156f4565b6068546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240160206040518083038186803b158015611a8f57600080fd5b505afa158015611aa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac79190615396565b1015611b2c5760405162461bcd60e51b815260206004820152602e60248201527f416d6f756e74206578636565647320617661696c61626c65207574696c697a6160448201526d1d1a5bdb88199bdc881c9bdd5b9960921b6064820152608401610bbb565b611c78565b606954821115611c30576068546040516370a0823160e01b81526001600160a01b03838116600483015260009216906370a082319060240160206040518083038186803b158015611b8157600080fd5b505afa158015611b95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bb99190615396565b9050848110611beb57606754606854611be6916001600160a01b03918216918591610100900416886149c6565b611c2a565b6000611bf782876156f4565b9050611c04818486614bff565b606754606854611c28916001600160a01b03918216918691610100900416896149c6565b505b50611c78565b81600114611c6f5760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59149bdd5b9960a21b6044820152606401610bbb565b611c7884614cf3565b5060008181526072602090815260408083208054600180820183559185528385200180546001600160a01b0319166001600160a01b038a1690811790915594845260738352818420948452939091529020805460ff191690911790556066548114611cf55760405162461bcd60e51b8152600401610bbb906155ed565b505050565b611d02614840565b6001600160a01b038116611d285760405162461bcd60e51b8152600401610bbb90615666565b607a80546001600160a01b0319166001600160a01b0383169081179091556040519081527faaf6f0738515c3cf390f1b3faec649d2dacb169d024089afc43bbe2fe66cd2d890602001610c9e565b611d7e614840565b606854600160a01b900460ff1615611de65760405162461bcd60e51b815260206004820152602560248201527f43616e2774206368616e676520726f756e64206c656e677468206166746572206044820152641cdd185c9d60da1b6064820152608401610bbb565b606a8190556040518181527f1d1fb7111c3779798bd4aefb2daea07ee8257a13c7eaceba89b4b1ccd405050d90602001610c9e565b600054610100900460ff16611e365760005460ff1615611e3a565b303b155b611e9d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bbb565b600054610100900460ff16158015611ebf576000805461ffff19166101011790555b611ecf61053a602084018461510d565b611ed761436b565b611ee7604083016020840161510d565b606780546001600160a01b039290921661010002610100600160a81b0319909216919091179055611f1e606083016040840161510d565b606880546001600160a01b0319166001600160a01b03929092169190911790556060820135606a55608082013560765560a082013560775560c0820135607855611f6f610100830160e08401615256565b6082805460ff19169115159190911790556068546001600160a01b031663095ea7b3611fa1604085016020860161510d565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526000196024820152604401602060405180830381600087803b158015611fea57600080fd5b505af1158015611ffe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120229190615272565b50600160695580156114d3576000805461ff00191690555050565b6001546001600160a01b031633146120b55760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610bbb565b60005460015460408051620100009093046001600160a01b03908116845290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a1600180546000805462010000600160b01b0319166001600160a01b03831662010000021790556001600160a01b0319169055565b612142614840565b608980546001600160a01b0319166001600160a01b038416908117909155608a82905560408051918252602082018390527fa1a8623472ca4e2879372be60dfd1ff0675778e49f7379eedd98ca57cf36b21a910160405180910390a15050565b6121aa614840565b6001600160a01b0381166121d05760405162461bcd60e51b8152600401610bbb90615666565b607b80546001600160a01b0319166001600160a01b0383169081179091556040519081527f3c7faa500efbd341aedbf1ee7ebd52ea36d226dda76bc01dd39b43d46f55b8d390602001610c9e565b612226614840565b6001600160a01b03811661224c5760405162461bcd60e51b8152600401610bbb90615666565b60678054610100600160a81b0319166101006001600160a01b038481168202929092179283905560685460405163095ea7b360e01b81529190930482166004820152600019602482015291169063095ea7b390604401602060405180830381600087803b1580156122bc57600080fd5b505af11580156122d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122f49190615272565b506040516001600160a01b03821681527f576297e5fcc8cd907ee80b240284865eb3d821bdc5232e6ee9e4d78a12531c0990602001610c9e565b600160666000828254612341919061569d565b909155505060665460345460ff161561236c5760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff161561238f5760405162461bcd60e51b8152600401610bbb9061550e565b6123976143c9565b6123e35760405162461bcd60e51b815260206004820152601960248201527f43616e277420636c6f73652063757272656e7420726f756e64000000000000006044820152606401610bbb565b6123eb61400f565b6069546000908152606c60205260408082205460685491516370a0823160e01b81526001600160a01b0391821660048201819052939291909116906370a082319060240160206040518083038186803b15801561244757600080fd5b505afa15801561245b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247f9190615396565b60695460009081526070602052604090205490915081111561254757608a546069546000908152607060205260408120549091670de0b6b3a7640000916124c690856156f4565b6124d091906156d5565b6124da91906156b5565b6089546068549192506124fc916001600160a01b0390811691869116846149c6565b61250681836156f4565b608a5460408051918252602082018490529193507fb8379d05082aa2dd32963972d72cc2d46257811133341855b63bb2fddda6ca3e910160405180910390a1505b606954600090815260706020526040902054612577576069546000908152607460205260409020600190556125b8565b60695460009081526070602052604090205461259b670de0b6b3a7640000836156d5565b6125a591906156b5565b6069546000908152607460205260409020555b6084805460ff191660011790556069546040517fa224cce482b24082d1d3128437615f7f5ce87b97453a11114846ea442a100272916125fa9190815260200190565b60405180910390a150506066548114610d145760405162461bcd60e51b8152600401610bbb906155ed565b600160666000828254612638919061569d565b9091555050606654606854600160a01b900460ff166126695760405162461bcd60e51b8152600401610bbb90615470565b3360009081526071602052604090205460ff16156126c95760405162461bcd60e51b815260206004820152601c60248201527f5769746864726177616c20616c726561647920726571756573746564000000006044820152606401610bbb565b6069546000908152606f602090815260408083203384529091529020546127285760405162461bcd60e51b81526020600482015260136024820152724e6f7468696e6720746f20776974686472617760681b6044820152606401610bbb565b606f6000606954600161273b919061569d565b815260208082019290925260409081016000908120338252909252902054156127765760405162461bcd60e51b8152600401610bbb90615555565b60345460ff16156127995760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16156127bc5760405162461bcd60e51b8152600401610bbb9061550e565b6127ce662386f26fc10000600a6156d5565b82101580156127ee57506127ea662386f26fc10000605a6156d5565b8211155b6128465760405162461bcd60e51b815260206004820152602360248201527f53686172652068617320746f206265206265747765656e2031302520616e642060448201526239302560e81b6064820152608401610bbb565b6069546000908152606f60209081526040808320338452909152812054670de0b6b3a7640000906128789085906156d5565b61288291906156b5565b905080607f5411156128ab5780607f60008282546128a091906156f4565b909155506128b19050565b6000607f555b336000818152607160209081526040808320805460ff19166001179055608682529182902086905590519182527fe5892ff2a8b08efb903ffbba1f0514c1d3e22eea34dd5b89cf30aabce03dde5a910160405180910390a15060665481146114d35760405162461bcd60e51b8152600401610bbb906155ed565b6001600160a01b03811660009081526087602052604090205480612b5d57816000805b826001600160a01b031663081ad4946040518163ffffffff1660e01b815260040160206040518083038186803b15801561298757600080fd5b505afa15801561299b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129bf9190615396565b811015612b59576040516302042e1d60e31b8152600481018290526001600160a01b0384169063102170e8906024016101006040518083038186803b158015612a0757600080fd5b505afa158015612a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a3f9190615154565b505060408051639e3b34bf60e01b815281519799508997600097506001600160a01b0389169650639e3b34bf9550600480830195509293509190829003018186803b158015612a8d57600080fd5b505afa158015612aa1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ac591906153fe565b509050606b54811115612b3f5782612b0357606a54606b54612ae790836156f4565b612af191906156b5565b612afc90600261569d565b9550612b44565b85606a54606b5483612b1591906156f4565b612b1f91906156b5565b612b2a90600261569d565b14612b3a57600195505050612b59565b612b44565b600195505b50508080612b5190615737565b91505061294e565b5050505b919050565b33600090815260716020526040902054819060ff1615612bd45760405162461bcd60e51b815260206004820152602760248201527f5769746864726177616c206973207265717565737465642c2063616e6e6f742060448201526619195c1bdcda5d60ca1b6064820152608401610bbb565b60765481607f54612be5919061569d565b1115612c3d5760405162461bcd60e51b815260206004820152602160248201527f4465706f73697420616d6f756e74206578636565647320414d4d204c502063616044820152600760fc1b6064820152608401610bbb565b6069546000908152606f60209081526040808320338452909152902054158015612c955750606f60006069546001612c75919061569d565b815260208082019290925260409081016000908120338252909252902054155b15612cf657607754811015612cf65760405162461bcd60e51b815260206004820152602160248201527f416d6f756e74206c657373207468616e206d696e4465706f736974416d6f756e6044820152601d60fa1b6064820152608401610bbb565b600160666000828254612d09919061569d565b909155505060665460345460ff1615612d345760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff1615612d575760405162461bcd60e51b8152600401610bbb9061550e565b60006069546001612d68919061569d565b90506000612d7582614a20565b606854909150612d90906001600160a01b03163383886149c6565b607a546001600160a01b0316331415612e085760405162461bcd60e51b815260206004820152603460248201527f43616e2774206465706f736974206469726563746c792061732064656661756c6044820152733a103634b8bab4b234ba3c90383937bb34b232b960611b6064820152608401610bbb565b6069546000908152606f60209081526040808320338452909152902054158015612e4957506000828152606f60209081526040808320338452909152902054155b15612ee25760785460795410612ea15760405162461bcd60e51b815260206004820152601b60248201527f4d617820616d6f756e74206f66207573657273207265616368656400000000006044820152606401610bbb565b6000828152606d602090815260408220805460018181018355918452919092200180546001600160a01b03191633179055607954612ede9161569d565b6079555b6000828152606f6020908152604080832033845290915281208054879290612f0b90849061569d565b909155505060008281526070602052604081208054879290612f2e90849061569d565b9250508190555084607f6000828254612f47919061569d565b9091555050607b546001600160a01b031615612fc257607b546040516302c7739b60e01b8152336004820152602481018790526001600160a01b03909116906302c7739b90604401600060405180830381600087803b158015612fa957600080fd5b505af1158015612fbd573d6000803e3d6000fd5b505050505b606954604080513381526020810188905280820192909252517f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca9181900360600190a150506066548114611cf55760405162461bcd60e51b8152600401610bbb906155ed565b60008080805b6069546000908152607260205260409020548110156131cf57606954600090815260726020526040902080548290811061307857634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015460695483526083825260408084206001600160a01b039092168085529190925291205490925060ff166131bd578192506000836001600160a01b03166337783bfa6040518163ffffffff1660e01b815260040160006040518083038186803b1580156130f157600080fd5b505afa158015613105573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261312d919081019061528e565b5090508080156131ab5750836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561317157600080fd5b505afa158015613185573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131a99190615272565b155b156131bb57600194505050505090565b505b806131c781615737565b91505061302e565b5060009250505090565b6001606660008282546131ec919061569d565b909155505060665460345460ff16156132175760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff161561323a5760405162461bcd60e51b8152600401610bbb9061550e565b6000821161325a5760405162461bcd60e51b8152600401610bbb906155ab565b6069546000908152606c60205260408120546001600160a01b03169080805b6069546000908152607260205260409020548110156135b0578583141561329f576135b0565b60695460009081526072602052604081208054839081106132d057634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015460695483526083825260408084206001600160a01b039092168085529190925291205490915060ff1661359d578092506000836001600160a01b03166337783bfa6040518163ffffffff1660e01b815260040160006040518083038186803b15801561334957600080fd5b505afa15801561335d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613385919081019061528e565b5090508080156134035750836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156133c957600080fd5b505afa1580156133dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134019190615272565b155b1561346e5760675460405163ad385b9160e01b81526001600160a01b0384811660048301526101009092049091169063ad385b9190602401600060405180830381600087803b15801561345557600080fd5b505af1158015613469573d6000803e3d6000fd5b505050505b836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156134a757600080fd5b505afa1580156134bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134df9190615272565b806135565750836001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b15801561351e57600080fd5b505afa158015613532573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135569190615272565b1561359b5760695460009081526083602090815260408083206001600160a01b03861684529091529020805460ff19166001908117909155613598908661569d565b94505b505b50806135a881615737565b915050613279565b5050505060665481146114d35760405162461bcd60e51b8152600401610bbb906155ed565b6135dd614840565b606854600160a01b900460ff16156136425760405162461bcd60e51b815260206004820152602260248201527f4c697175696469747920706f6f6c2068617320616c7265616479207374617274604482015261195960f21b6064820152608401610bbb565b600260005260706020527f60b45511bf7ff7962f4d95774002c49705feb8f9eb913220abee2cd301af1d1d546136ba5760405162461bcd60e51b815260206004820152601d60248201527f63616e206e6f7420737461727420776974682030206465706f736974730000006044820152606401610bbb565b42606b55600260698190556000906136d190614a20565b9050806001600160a01b0316637d3de7ce606b546136ef6002610b3b565b6040516001600160e01b031960e085901b16815260048101929092526024820152604401600060405180830381600087803b15801561372d57600080fd5b505af1158015613741573d6000803e3d6000fd5b50506068805460ff60a01b1916600160a01b17905550506040517f960682678fca98f3ed131eaf165e59544bcd738e948f0b3c64f58fa9e1c65e6090600090a150565b6000606c60006137938461292b565b81526020810191909152604001600020546001600160a01b031692915050565b6137bb614840565b6001600160a01b0381166138035760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610bbb565b600154600160a81b900460ff16156138535760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610bbb565b600080546001600160a01b038381166201000081810262010000600160b01b031990941693909317938490556001805460ff60a81b1916600160a81b1790556040805193909404909116825260208201527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9101610c9e565b60008281526075602081815260408084205460748352818520548686529390925283205490916138fb916156d5565b61390591906156b5565b9392505050565b60345460ff161561392f5760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16156139525760405162461bcd60e51b8152600401610bbb9061550e565b60675461010090046001600160a01b031633146139815760405162461bcd60e51b8152600401610bbb9061549e565b600061398c8361292b565b9050600060018211156139a7576139a282614a20565b6139b4565b607a546001600160a01b03165b6067546068549192506139da916001600160a01b039081169161010090041683866149c6565b60008281526073602090815260408083206001600160a01b038816845290915290205460ff1615613a335760008281526083602090815260408083206001600160a01b03881684529091529020805460ff191660011790555b50505050565b6069546000908152606f602090815260408083206001600160a01b0385168452909152812054151580613ab057506000606f60006069546001613a7c919061569d565b81526020019081526020016000206000846001600160a01b03166001600160a01b0316815260200190815260200160002054115b8015610b6357506001600160a01b03821660009081526071602052604090205460ff161580610b635750506001600160a01b0316600090815260866020526040902054151590565b613b00614840565b60778190556040518181527f990717cc219e5348c1b88bb0ff530d804f0b6f54f3b03844a2bfbe4eb1e9c5d690602001610c9e565b606a54600090610b4c6002846156f4565b613b4e614840565b60768190556040518181527f8c43aa02599ac8f8bab4724621ceea5e7a06b07bbbfaf3b7bd0386cbe481ea3c90602001610c9e565b600160666000828254613b96919061569d565b909155505060665460345460ff1615613bc15760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16613c135760405162461bcd60e51b815260206004820152601a60248201527f526f756e6420636c6f73696e67206e6f742070726570617265640000000000006044820152606401610bbb565b6069546000908152606d602052604090205460855414613c755760405162461bcd60e51b815260206004820152601b60248201527f4e6f7420616c6c2075736572732070726f6365737365642079657400000000006044820152606401610bbb565b6084805460ff191690556069546000908152606c6020908152604080832054606f8352818420607a546001600160a01b039081168652935292205491169015613d6f57606954600090815260746020908152604080832054606f8352818420607a546001600160a01b03168552909252822054670de0b6b3a764000091613cfb916156d5565b613d0591906156b5565b607a54606854919250613d27916001600160a01b0390811691859116846149c6565b607a54604080516001600160a01b039092168252602082018390527fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a910160405180910390a1505b60695460021415613d9d57606954600090815260746020908152604080832054607590925290912055613e00565b606954600081815260746020526040812054670de0b6b3a7640000929091607591613dca906001906156f4565b815260200190815260200160002054613de391906156d5565b613ded91906156b5565b6069546000908152607560205260409020555b606960008154613e0f90615737565b909155506068546040516370a0823160e01b81526001600160a01b038381166004830152909116906370a082319060240160206040518083038186803b158015613e5857600080fd5b505afa158015613e6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e909190615396565b60695460009081526070602052604081208054909190613eb190849061569d565b90915550506069546000818152606f60209081526040808320607a546001600160a01b031684528252808320549383526070909152902054613ef391906156f4565b607f55606954600090613f0590614a20565b6068546040516370a0823160e01b81526001600160a01b038086166004830152929350613fa092859285929116906370a082319060240160206040518083038186803b158015613f5457600080fd5b505afa158015613f68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8c9190615396565b6068546001600160a01b03169291906149c6565b60006085556069547fc67dda8e11f1aa941c7e74466b1859a07a32f46aaf641d29b83be348424d93cd90613fd6906001906156f4565b607460006001606954613fe991906156f4565b8152602001908152602001600020546040516125fa929190918252602082015260400190565b60845460ff16156140325760405162461bcd60e51b8152600401610bbb9061550e565b6069546000908152606c60205260408120546001600160a01b03169080805b606954600090815260726020526040902054811015613a3357606954600090815260726020526040902080548290811061409b57634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015460695483526083825260408084206001600160a01b039092168085529190925291205490925060ff16614359578192506000836001600160a01b03166337783bfa6040518163ffffffff1660e01b815260040160006040518083038186803b15801561411457600080fd5b505afa158015614128573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052614150919081019061528e565b5090508080156141ce5750836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561419457600080fd5b505afa1580156141a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141cc9190615272565b155b156142395760675460405163ad385b9160e01b81526001600160a01b0385811660048301526101009092049091169063ad385b9190602401600060405180830381600087803b15801561422057600080fd5b505af1158015614234573d6000803e3d6000fd5b505050505b836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561427257600080fd5b505afa158015614286573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142aa9190615272565b806143215750836001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b1580156142e957600080fd5b505afa1580156142fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143219190615272565b156143575760695460009081526083602090815260408083206001600160a01b03871684529091529020805460ff191660011790555b505b8061436381615737565b915050614051565b60675460ff16156143b45760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b6044820152606401610bbb565b6067805460ff19166001908117909155606655565b606854600090600160a01b900460ff1615806143ee57506143eb606954610b3b565b42105b156143f95750600090565b6000805b60695460009081526072602052604090205481101561451757606954600090815260726020526040812080548390811061444757634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015460695483526083825260408084206001600160a01b039092168085529190925291205490915060ff1661450457809250826001600160a01b031663e990b0986040518163ffffffff1660e01b815260040160206040518083038186803b1580156144be57600080fd5b505afa1580156144d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144f69190615272565b614504576000935050505090565b508061450f81615737565b9150506143fd565b50600191505090565b600160666000828254614533919061569d565b909155505060665460345460ff161561455e5760405162461bcd60e51b8152600401610bbb906154e4565b614566614840565b60005b8281101561474b57600084848381811061459357634e487b7160e01b600052603260045260246000fd5b905060200201359050600181116145ec5760405162461bcd60e51b815260206004820152601e60248201527f43616e27742070756c6c2066726f6d2064656661756c7420726f756e647300006044820152606401610bbb565b606954811061464e5760405162461bcd60e51b815260206004820152602860248201527f43616e27742070756c6c2066726f6d2063757272656e74206f722066757475726044820152676520726f756e647360c01b6064820152608401610bbb565b6000818152606c60205260408082205460685491516370a0823160e01b81526001600160a01b0391821660048201819052939291909116906370a082319060240160206040518083038186803b1580156146a757600080fd5b505afa1580156146bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146df9190615396565b6068549091506146fa906001600160a01b03168333846149c6565b7f772d8b935fea383cb27d439d54ed82e6d7b3f56a51d5f2e1c06d59365cd75d6260695460405161472d91815260200190565b60405180910390a1505050808061474390615737565b915050614569565b506066548114611cf55760405162461bcd60e51b8152600401610bbb906155ed565b60675460009061010090046001600160a01b0316331461479f5760405162461bcd60e51b8152600401610bbb9061549e565b6001606660008282546147b2919061569d565b909155505060665460345460ff16156147dd5760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16156148005760405162461bcd60e51b8152600401610bbb9061550e565b600061480b8461292b565b905061481681614a20565b925050606654811461483a5760405162461bcd60e51b8152600401610bbb906155ed565b50919050565b6000546201000090046001600160a01b031633146148b85760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610bbb565b565b60345460ff166149035760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610bbb565b6034805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b60345460ff16156149705760405162461bcd60e51b8152600401610bbb906154e4565b6034805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586149303390565b60825460009060ff16156149c257610b6364e8d4a51000836156b5565b5090565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052613a33908590614e20565b6000818152606c60205260409020546001600160a01b031680612b5d578160011415614a7b5750607a80546000838152606c6020526040902080546001600160a01b0319166001600160a01b03928316179055905416919050565b607d546001600160a01b0316614ad35760405162461bcd60e51b815260206004820152601d60248201527f526f756e6420706f6f6c206d6173746572636f7079206e6f74207365740000006044820152606401610bbb565b607d54600090614aeb906001600160a01b0316614ef2565b6068549091506001600160a01b038083169163d13f90b49130911686614b156105196001836156f4565b614b1e89610b3b565b6040516001600160e01b031960e088901b1681526001600160a01b03958616600482015294909316602485015260448401919091526064830152608482015260a401600060405180830381600087803b158015614b7a57600080fd5b505af1158015614b8e573d6000803e3d6000fd5b5050506000848152606c602090815260409182902080546001600160a01b0319166001600160a01b03861690811790915582518781529182015292935083927f24c1b21b902a85b5039d7d72427d9376657229eea77880ec9e62031dc950f6ba92500160405180910390a150919050565b607a546001600160a01b0316614c275760405162461bcd60e51b8152600401610bbb90615624565b607a54606854614c45916001600160a01b03918216911684866149c6565b6000818152606f60209081526040808320607a546001600160a01b0316845290915281208054859290614c7990849061569d565b909155505060008181526070602052604081208054859290614c9c90849061569d565b9091555050607a54604080516001600160a01b0390921682526020820185905281018290527f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca9060600160405180910390a1505050565b607a546001600160a01b0316614d1b5760405162461bcd60e51b8152600401610bbb90615624565b607a54606754606854614d45926001600160a01b03918216929082169161010090910416846149c6565b607a546001600160a01b031660009081527f6e075724c97aa8b42d86406682b25042490cf37e8d91270a79e54ad319c8b085602052604081208054839290614d8e90849061569d565b90915550506001600090815260706020527fb1a24bae1e5047fbb0cf526090cbec15c09a4036896111a8964a155b1c4771a18054839290614dd090849061569d565b9091555050607a54604080516001600160a01b039092168252602082018390526001908201527f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca90606001610c9e565b6000614e75826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614f8a9092919063ffffffff16565b805190915015611cf55780806020019051810190614e939190615272565b611cf55760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bbb565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528260601b60148201526e5af43d82803e903d91602b57fd5bf360881b60288201526037816000f09150506001600160a01b038116612b5d5760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b6044820152606401610bbb565b6060614f998484600085614fa1565b949350505050565b6060824710156150025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610bbb565b843b6150505760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bbb565b600080866001600160a01b0316858760405161506c9190615421565b60006040518083038185875af1925050503d80600081146150a9576040519150601f19603f3d011682016040523d82523d6000602084013e6150ae565b606091505b50915091506150be8282866150c9565b979650505050505050565b606083156150d8575081613905565b8251156150e85782518084602001fd5b8160405162461bcd60e51b8152600401610bbb919061543d565b8051612b5d81615793565b60006020828403121561511e578081fd5b81356139058161577e565b6000806040838503121561513b578081fd5b82356151468161577e565b946020939093013593505050565b600080600080600080600080610100898b031215615170578384fd5b885161517b8161577e565b8098505060208901519650604089015195506060890151945060808901516151a281615793565b60a08a01519094506151b381615793565b60c08a01519093506151c481615793565b60e08a01519092506151d581615793565b809150509295985092959890939650565b600080602083850312156151f8578182fd5b823567ffffffffffffffff8082111561520f578384fd5b818501915085601f830112615222578384fd5b813581811115615230578485fd5b8660208260051b8501011115615244578485fd5b60209290920196919550909350505050565b600060208284031215615267578081fd5b813561390581615793565b600060208284031215615283578081fd5b815161390581615793565b600080604083850312156152a0578182fd5b82516152ab81615793565b8092505060208084015167ffffffffffffffff808211156152ca578384fd5b818601915086601f8301126152dd578384fd5b8151818111156152ef576152ef615768565b8060051b604051601f19603f8301168101818110858211171561531457615314615768565b604052828152858101935084860182860187018b1015615332578788fd5b8795505b8386101561535b5761534781615102565b855260019590950194938601938601615336565b508096505050505050509250929050565b6000610100828403121561483a578081fd5b60006020828403121561538f578081fd5b5035919050565b6000602082840312156153a7578081fd5b5051919050565b600080604083850312156153c0578182fd5b8235915060208301356153d28161577e565b809150509250929050565b600080604083850312156153ef578182fd5b50508035926020909101359150565b60008060408385031215615410578182fd5b505080516020909101519092909150565b6000825161543381846020870161570b565b9190910192915050565b602081526000825180602084015261545c81604085016020870161570b565b601f01601f19169190910160400192915050565b602080825260149082015273141bdbdb081a185cc81b9bdd081cdd185c9d195960621b604082015260600190565b60208082526026908201527f6f6e6c792074686520414d4d206d617920706572666f726d207468657365206d6040820152656574686f647360d01b606082015260800190565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b60208082526027908201527f4e6f7420616c6c6f77656420647572696e6720726f756e64436c6f73696e67506040820152661c995c185c995960ca1b606082015260800190565b60208082526036908201527f43616e277420776974686472617720617320796f7520616c72656164792064656040820152751c1bdcda5d195908199bdc881b995e1d081c9bdd5b9960521b606082015260800190565b60208082526022908201527f626174636853697a652068617320746f2062652067726561746572207468616e604082015261020360f41b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526022908201527f64656661756c74206c69717569646974792070726f7669646572206e6f742073604082015261195d60f21b606082015260800190565b6020808252601b908201527f43616e206e6f74207365742061207a65726f2061646472657373210000000000604082015260600190565b600082198211156156b0576156b0615752565b500190565b6000826156d057634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156156ef576156ef615752565b500290565b60008282101561570657615706615752565b500390565b60005b8381101561572657818101518382015260200161570e565b83811115613a335750506000910152565b600060001982141561574b5761574b615752565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610d1457600080fd5b8015158114610d1457600080fdfea2646970667358221220120375353c99bca4c8e52985ed0a7882dc61743854949eed12750849aed22b1464736f6c63430008040033
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061048b5760003560e01c80637a1e0aa811610262578063be9a655511610151578063ddcc8fe9116100ce578063efcac68911610092578063efcac68914610a7e578063f475f13b14610a91578063f61fcb8b14610aa4578063fd8a8cc614610ac4578063fdaf17f014610ad7578063ff50abdc14610afa57600080fd5b8063ddcc8fe914610a4b578063e278fe6f14610a5e578063e3041fd914610a66578063ebc7977214610a6e578063ee161cce14610a7657600080fd5b8063d69fb66811610115578063d69fb668146109f6578063d7efa129146109ff578063d95ad45c14610a12578063db7f92d414610a25578063ddc6ac2314610a3857600080fd5b8063be9a6555146109ac578063c2edfc73146109b4578063c3b83f5f146109c7578063c9f4ff46146109da578063d27c0797146109ed57600080fd5b80639324cac7116101df578063b6b55f25116101a3578063b6b55f2514610962578063b745abe314610975578063b9b1be8b1461097d578063bcfa893714610990578063bdcc22e9146109a357600080fd5b80639324cac7146108fc5780639bd2e61b1461090f5780639faf680214610922578063a8df539f14610935578063b01862151461094257600080fd5b80638b649b94116102265780638b649b94146108bc5780638b844412146108c55780638da5cb5b146108cd5780638fe812b4146108e6578063930102f2146108f357600080fd5b80637a1e0aa8146108485780637d550e051461085b5780637f7b8c50146108735780637f8525821461089657806384c095c2146108a957600080fd5b806348663e951161037e578063634e0d97116102fb5780636c321c8a116102bf5780636c321c8a146107d657806371143ab9146107df5780637261b81a1461080d57806374094edd1461082057806379ba50971461084057600080fd5b8063634e0d9714610762578063645006ca1461079057806365e0e725146107995780636685fdc2146107ac578063681312f5146107c357600080fd5b806358c09cc01161034257806358c09cc0146107075780635c7b396e1461071a5780635c975abb146107235780635ddd3e831461072e578063610589e11461075957600080fd5b806348663e951461068e5780634ae7937f146106a15780634b141b3f146106c15780634d549a42146106e157806353a47bb7146106f457600080fd5b80631baa88561161040c578063336d30ed116103d0578063336d30ed1461060c578063343e4f9f1461062c5780633b92d7581461063f57806340774ff6146106525780634651f0801461066557600080fd5b80631baa8856146105b15780631daae173146105ba5780631f2698ab146105dd578063202ffce8146105f1578063311c56df1461060457600080fd5b8063146ca53111610453578063146ca531146105415780631627540c1461054a57806316c38b3c1461055d578063175e6700146105705780631b2a52d81461059e57600080fd5b806302f97b0c14610490578063042047cf146104d3578063082f9fd4146104e057806312b19a131461050b57806313af40351461052c575b600080fd5b6104be61049e3660046153ae565b608360209081526000928352604080842090915290825290205460ff1681565b60405190151581526020015b60405180910390f35b6080546104be9060ff1681565b6104f36104ee3660046153dd565b610b03565b6040516001600160a01b0390911681526020016104ca565b61051e61051936600461537e565b610b3b565b6040519081526020016104ca565b61053f61053a36600461510d565b610b69565b005b61051e60695481565b61053f61055836600461510d565b610ca9565b61053f61056b366004615256565b610cff565b61058361057e36600461510d565b610d1f565b604080519384526020840192909252908201526060016104ca565b61053f6105ac36600461537e565b610e89565b61051e606b5481565b6104be6105c836600461510d565b60716020526000908152604090205460ff1681565b6068546104be90600160a01b900460ff1681565b61053f6105ff36600461537e565b6114d7565b61053f611514565b61051e61061a36600461537e565b60756020526000908152604090205481565b6104f361063a3660046153dd565b611793565b607a546104f3906001600160a01b031681565b61053f61066036600461537e565b6117af565b6104f361067336600461537e565b606c602052600090815260409020546001600160a01b031681565b6089546104f3906001600160a01b031681565b61051e6106af36600461537e565b60706020526000908152604090205481565b61051e6106cf36600461537e565b60009081526072602052604090205490565b61053f6106ef36600461510d565b6117ec565b6001546104f3906001600160a01b031681565b61053f610715366004615129565b611868565b61051e60855481565b60345460ff166104be565b61051e61073c3660046153ae565b606f60209081526000928352604080842090915290825290205481565b61051e60785481565b6104be6107703660046153ae565b606e60209081526000928352604080842090915290825290205460ff1681565b61051e60775481565b61053f6107a736600461510d565b611cfa565b6069546000908152606d602052604090205461051e565b61053f6107d136600461537e565b611d76565b61051e60885481565b6104be6107ed3660046153ae565b607360209081526000928352604080842090915290825290205460ff1681565b61053f61081b36600461536c565b611e1b565b61051e61082e36600461537e565b60746020526000908152604090205481565b61053f61203d565b61053f610856366004615129565b61213a565b6067546104f39061010090046001600160a01b031681565b6104be61088136600461510d565b607e6020526000908152604090205460ff1681565b61053f6108a436600461510d565b6121a2565b61053f6108b736600461510d565b61221e565b61051e606a5481565b61053f61232e565b6000546104f3906201000090046001600160a01b031681565b6082546104be9060ff1681565b61051e607c5481565b6068546104f3906001600160a01b031681565b61053f61091d36600461537e565b612625565b61051e61093036600461510d565b61292b565b6084546104be9060ff1681565b61051e61095036600461510d565b60876020526000908152604090205481565b61053f61097036600461537e565b612b62565b6104be613028565b607d546104f3906001600160a01b031681565b61053f61099e36600461537e565b6131d9565b61051e60795481565b61053f6135d5565b6104f36109c236600461510d565b613784565b61053f6109d536600461510d565b6137b3565b61051e6109e83660046153dd565b6138cc565b61051e60765481565b61051e608a5481565b61053f610a0d366004615129565b61390c565b6104be610a2036600461510d565b613a39565b61053f610a3336600461537e565b613af8565b61051e610a4636600461537e565b613b35565b61053f610a5936600461537e565b613b46565b61053f613b83565b61053f61400f565b61053f61436b565b6104be6143c9565b61053f610a8c3660046151e6565b614520565b6104f3610a9f36600461510d565b61476d565b61051e610ab236600461510d565b60866020526000908152604090205481565b607b546104f3906001600160a01b031681565b6104be610ae536600461510d565b60816020526000908152604090205460ff1681565b61051e607f5481565b60726020528160005260406000208181548110610b1f57600080fd5b6000918252602090912001546001600160a01b03169150829050565b606a54600090610b4c6001846156f4565b610b5691906156d5565b606b54610b63919061569d565b92915050565b6001600160a01b038116610bc45760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f7420626520300000000000000060448201526064015b60405180910390fd5b600154600160a01b900460ff1615610c305760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610bbb565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b03831662010000810262010000600160b01b03199092169190911782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91015b60405180910390a150565b610cb1614840565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290602001610c9e565b610d07614840565b80610d1757610d146148ba565b50565b610d1461494d565b6000806000806069546001610d34919061569d565b607b54604051631676539160e01b81526001600160a01b03888116600483015292935091169063167653919060240160206040518083038186803b158015610d7b57600080fd5b505afa158015610d8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610db39190615396565b9150610dde670de0b6b3a7640000607c5484610dcf91906156d5565b610dd991906156b5565b6149a5565b6000828152606f602081815260408084206001600160a01b038b1680865290835281852054606954865293835281852090855290915290912054919550610e249161569d565b8411610e31576000610e7f565b6000818152606f602081815260408084206001600160a01b038a1680865290835281852054606954865293835281852090855290915290912054610e7590866156f4565b610e7f91906156f4565b9250509193909250565b600160666000828254610e9c919061569d565b909155505060665460345460ff1615610ec75760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16610f195760405162461bcd60e51b815260206004820152601a60248201527f526f756e6420636c6f73696e67206e6f742070726570617265640000000000006044820152606401610bbb565b6069546000908152606d602052604090205460855410610f7b5760405162461bcd60e51b815260206004820152601b60248201527f416c6c20757365727320616c72656164792070726f63657373656400000000006044820152606401610bbb565b60008211610f9b5760405162461bcd60e51b8152600401610bbb906155ab565b6069546000908152606c60205260408120546085546001600160a01b039091169190610fc890859061569d565b6069546000908152606d6020526040902054909150811115610ff857506069546000908152606d60205260409020545b6085545b81811015611473576069546000908152606d6020526040812080548390811061103557634e487b7160e01b600052603260045260246000fd5b6000918252602080832090910154606954835260748252604080842054606f84528185206001600160a01b0390931680865292909352832054909350670de0b6b3a764000091611084916156d5565b61108e91906156b5565b6001600160a01b03831660009081526071602052604090205490915060ff161580156110ca575060695460009081526074602052604090205415155b1561122f5780606f600060695460016110e3919061569d565b81526020019081526020016000206000846001600160a01b03166001600160a01b031681526020019081526020016000205461111f919061569d565b606f60006069546001611132919061569d565b81526020019081526020016000206000846001600160a01b03166001600160a01b0316815260200190815260200160002081905550606d6000606954600161117a919061569d565b8152602080820192909252604001600090812080546001810182559082529190200180546001600160a01b0319166001600160a01b0384811691909117909155607b54161561122a57607b546040516302c7739b60e01b81526001600160a01b03848116600483015260248201849052909116906302c7739b90604401600060405180830381600087803b15801561121157600080fd5b505af1158015611225573d6000803e3d6000fd5b505050505b61144d565b6001600160a01b038216600090815260866020526040902054156113a5576001600160a01b038216600090815260866020526040812054670de0b6b3a76400009061127a90846156d5565b61128491906156b5565b60685490915061129f906001600160a01b03168785846149c6565b604080516001600160a01b0385168152602081018390527fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a910160405180910390a16001600160a01b0383166000908152607160209081526040808320805460ff1916905560869091528120819055606954606d919061132090600161569d565b8152602080820192909252604001600090812080546001810182559082529190200180546001600160a01b0319166001600160a01b03851617905561136581836156f4565b606f60006069546001611378919061569d565b8152602080820192909252604090810160009081206001600160a01b03881682529092529020555061144d565b6000606f600060695460016113ba919061569d565b8152602080820192909252604090810160009081206001600160a01b038088168352935220919091556068546113f391168684846149c6565b6001600160a01b038216600081815260716020908152604091829020805460ff19169055815192835282018390527fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a910160405180910390a15b60855461145b90600161569d565b6085555081905061146b81615737565b915050610ffc565b5060695460408051918252602082018690527f2e692c8fcabe33ba22535323e79dcb54ef22dccdb8e4ebdd9f2a7ffb1a28856c910160405180910390a1505060665481146114d35760405162461bcd60e51b8152600401610bbb906155ed565b5050565b6114df614840565b60788190556040518181527fe7c2c09f66c8b970b4a99250f4d0844e1496b9d51d4760a17b0134ddd52023e190602001610c9e565b600160666000828254611527919061569d565b9091555050606654606854600160a01b900460ff166115585760405162461bcd60e51b8152600401610bbb90615470565b3360009081526071602052604090205460ff16156115b85760405162461bcd60e51b815260206004820152601c60248201527f5769746864726177616c20616c726561647920726571756573746564000000006044820152606401610bbb565b6069546000908152606f602090815260408083203384529091529020546116175760405162461bcd60e51b81526020600482015260136024820152724e6f7468696e6720746f20776974686472617760681b6044820152606401610bbb565b606f6000606954600161162a919061569d565b815260208082019290925260409081016000908120338252909252902054156116655760405162461bcd60e51b8152600401610bbb90615555565b60345460ff16156116885760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16156116ab5760405162461bcd60e51b8152600401610bbb9061550e565b6069546000908152606f60209081526040808320338452909152902054607f54111561170c576069546000908152606f60209081526040808320338452909152812054607f8054919290916117019084906156f4565b909155506117129050565b6000607f555b600160795461172191906156f4565b60795533600081815260716020908152604091829020805460ff1916600117905590519182527fe5892ff2a8b08efb903ffbba1f0514c1d3e22eea34dd5b89cf30aabce03dde5a910160405180910390a16066548114610d145760405162461bcd60e51b8152600401610bbb906155ed565b606d6020528160005260406000208181548110610b1f57600080fd5b6117b7614840565b60888190556040518181527fc117ccf765672707ebe3c1606037488c1e27dc1f42b5266e3d6b496db7d4209e90602001610c9e565b6117f4614840565b6001600160a01b03811661181a5760405162461bcd60e51b8152600401610bbb90615666565b607d80546001600160a01b0319166001600160a01b0383169081179091556040519081527fa65a5aa86bb6f8e75752296da3ebda45474c8a302fc640c3b62868793862a03390602001610c9e565b60016066600082825461187b919061569d565b909155505060665460345460ff16156118a65760405162461bcd60e51b8152600401610bbb906154e4565b60675461010090046001600160a01b031633146118d55760405162461bcd60e51b8152600401610bbb9061549e565b60845460ff16156118f85760405162461bcd60e51b8152600401610bbb9061550e565b606854600160a01b900460ff166119215760405162461bcd60e51b8152600401610bbb90615470565b600082116119715760405162461bcd60e51b815260206004820152601960248201527f43616e277420636f6d6d69742061207a65726f207472616465000000000000006044820152606401610bbb565b61197a826149a5565b60825490925060ff1661198d5781611998565b61199882600161569d565b915060006119a58461292b565b6001600160a01b03851660009081526087602052604081208290559091506119cc82614a20565b9050606954821415611b31576067546068546119fc916001600160a01b03918216918491610100900416876149c6565b608854606954600090815260706020526040902054670de0b6b3a764000091611a24916156d5565b611a2e91906156b5565b606954600090815260706020526040902054611a4a91906156f4565b6068546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240160206040518083038186803b158015611a8f57600080fd5b505afa158015611aa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac79190615396565b1015611b2c5760405162461bcd60e51b815260206004820152602e60248201527f416d6f756e74206578636565647320617661696c61626c65207574696c697a6160448201526d1d1a5bdb88199bdc881c9bdd5b9960921b6064820152608401610bbb565b611c78565b606954821115611c30576068546040516370a0823160e01b81526001600160a01b03838116600483015260009216906370a082319060240160206040518083038186803b158015611b8157600080fd5b505afa158015611b95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bb99190615396565b9050848110611beb57606754606854611be6916001600160a01b03918216918591610100900416886149c6565b611c2a565b6000611bf782876156f4565b9050611c04818486614bff565b606754606854611c28916001600160a01b03918216918691610100900416896149c6565b505b50611c78565b81600114611c6f5760405162461bcd60e51b815260206004820152600c60248201526b125b9d985b1a59149bdd5b9960a21b6044820152606401610bbb565b611c7884614cf3565b5060008181526072602090815260408083208054600180820183559185528385200180546001600160a01b0319166001600160a01b038a1690811790915594845260738352818420948452939091529020805460ff191690911790556066548114611cf55760405162461bcd60e51b8152600401610bbb906155ed565b505050565b611d02614840565b6001600160a01b038116611d285760405162461bcd60e51b8152600401610bbb90615666565b607a80546001600160a01b0319166001600160a01b0383169081179091556040519081527faaf6f0738515c3cf390f1b3faec649d2dacb169d024089afc43bbe2fe66cd2d890602001610c9e565b611d7e614840565b606854600160a01b900460ff1615611de65760405162461bcd60e51b815260206004820152602560248201527f43616e2774206368616e676520726f756e64206c656e677468206166746572206044820152641cdd185c9d60da1b6064820152608401610bbb565b606a8190556040518181527f1d1fb7111c3779798bd4aefb2daea07ee8257a13c7eaceba89b4b1ccd405050d90602001610c9e565b600054610100900460ff16611e365760005460ff1615611e3a565b303b155b611e9d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bbb565b600054610100900460ff16158015611ebf576000805461ffff19166101011790555b611ecf61053a602084018461510d565b611ed761436b565b611ee7604083016020840161510d565b606780546001600160a01b039290921661010002610100600160a81b0319909216919091179055611f1e606083016040840161510d565b606880546001600160a01b0319166001600160a01b03929092169190911790556060820135606a55608082013560765560a082013560775560c0820135607855611f6f610100830160e08401615256565b6082805460ff19169115159190911790556068546001600160a01b031663095ea7b3611fa1604085016020860161510d565b6040516001600160e01b031960e084901b1681526001600160a01b0390911660048201526000196024820152604401602060405180830381600087803b158015611fea57600080fd5b505af1158015611ffe573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120229190615272565b50600160695580156114d3576000805461ff00191690555050565b6001546001600160a01b031633146120b55760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610bbb565b60005460015460408051620100009093046001600160a01b03908116845290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a1600180546000805462010000600160b01b0319166001600160a01b03831662010000021790556001600160a01b0319169055565b612142614840565b608980546001600160a01b0319166001600160a01b038416908117909155608a82905560408051918252602082018390527fa1a8623472ca4e2879372be60dfd1ff0675778e49f7379eedd98ca57cf36b21a910160405180910390a15050565b6121aa614840565b6001600160a01b0381166121d05760405162461bcd60e51b8152600401610bbb90615666565b607b80546001600160a01b0319166001600160a01b0383169081179091556040519081527f3c7faa500efbd341aedbf1ee7ebd52ea36d226dda76bc01dd39b43d46f55b8d390602001610c9e565b612226614840565b6001600160a01b03811661224c5760405162461bcd60e51b8152600401610bbb90615666565b60678054610100600160a81b0319166101006001600160a01b038481168202929092179283905560685460405163095ea7b360e01b81529190930482166004820152600019602482015291169063095ea7b390604401602060405180830381600087803b1580156122bc57600080fd5b505af11580156122d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122f49190615272565b506040516001600160a01b03821681527f576297e5fcc8cd907ee80b240284865eb3d821bdc5232e6ee9e4d78a12531c0990602001610c9e565b600160666000828254612341919061569d565b909155505060665460345460ff161561236c5760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff161561238f5760405162461bcd60e51b8152600401610bbb9061550e565b6123976143c9565b6123e35760405162461bcd60e51b815260206004820152601960248201527f43616e277420636c6f73652063757272656e7420726f756e64000000000000006044820152606401610bbb565b6123eb61400f565b6069546000908152606c60205260408082205460685491516370a0823160e01b81526001600160a01b0391821660048201819052939291909116906370a082319060240160206040518083038186803b15801561244757600080fd5b505afa15801561245b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247f9190615396565b60695460009081526070602052604090205490915081111561254757608a546069546000908152607060205260408120549091670de0b6b3a7640000916124c690856156f4565b6124d091906156d5565b6124da91906156b5565b6089546068549192506124fc916001600160a01b0390811691869116846149c6565b61250681836156f4565b608a5460408051918252602082018490529193507fb8379d05082aa2dd32963972d72cc2d46257811133341855b63bb2fddda6ca3e910160405180910390a1505b606954600090815260706020526040902054612577576069546000908152607460205260409020600190556125b8565b60695460009081526070602052604090205461259b670de0b6b3a7640000836156d5565b6125a591906156b5565b6069546000908152607460205260409020555b6084805460ff191660011790556069546040517fa224cce482b24082d1d3128437615f7f5ce87b97453a11114846ea442a100272916125fa9190815260200190565b60405180910390a150506066548114610d145760405162461bcd60e51b8152600401610bbb906155ed565b600160666000828254612638919061569d565b9091555050606654606854600160a01b900460ff166126695760405162461bcd60e51b8152600401610bbb90615470565b3360009081526071602052604090205460ff16156126c95760405162461bcd60e51b815260206004820152601c60248201527f5769746864726177616c20616c726561647920726571756573746564000000006044820152606401610bbb565b6069546000908152606f602090815260408083203384529091529020546127285760405162461bcd60e51b81526020600482015260136024820152724e6f7468696e6720746f20776974686472617760681b6044820152606401610bbb565b606f6000606954600161273b919061569d565b815260208082019290925260409081016000908120338252909252902054156127765760405162461bcd60e51b8152600401610bbb90615555565b60345460ff16156127995760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16156127bc5760405162461bcd60e51b8152600401610bbb9061550e565b6127ce662386f26fc10000600a6156d5565b82101580156127ee57506127ea662386f26fc10000605a6156d5565b8211155b6128465760405162461bcd60e51b815260206004820152602360248201527f53686172652068617320746f206265206265747765656e2031302520616e642060448201526239302560e81b6064820152608401610bbb565b6069546000908152606f60209081526040808320338452909152812054670de0b6b3a7640000906128789085906156d5565b61288291906156b5565b905080607f5411156128ab5780607f60008282546128a091906156f4565b909155506128b19050565b6000607f555b336000818152607160209081526040808320805460ff19166001179055608682529182902086905590519182527fe5892ff2a8b08efb903ffbba1f0514c1d3e22eea34dd5b89cf30aabce03dde5a910160405180910390a15060665481146114d35760405162461bcd60e51b8152600401610bbb906155ed565b6001600160a01b03811660009081526087602052604090205480612b5d57816000805b826001600160a01b031663081ad4946040518163ffffffff1660e01b815260040160206040518083038186803b15801561298757600080fd5b505afa15801561299b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129bf9190615396565b811015612b59576040516302042e1d60e31b8152600481018290526001600160a01b0384169063102170e8906024016101006040518083038186803b158015612a0757600080fd5b505afa158015612a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a3f9190615154565b505060408051639e3b34bf60e01b815281519799508997600097506001600160a01b0389169650639e3b34bf9550600480830195509293509190829003018186803b158015612a8d57600080fd5b505afa158015612aa1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ac591906153fe565b509050606b54811115612b3f5782612b0357606a54606b54612ae790836156f4565b612af191906156b5565b612afc90600261569d565b9550612b44565b85606a54606b5483612b1591906156f4565b612b1f91906156b5565b612b2a90600261569d565b14612b3a57600195505050612b59565b612b44565b600195505b50508080612b5190615737565b91505061294e565b5050505b919050565b33600090815260716020526040902054819060ff1615612bd45760405162461bcd60e51b815260206004820152602760248201527f5769746864726177616c206973207265717565737465642c2063616e6e6f742060448201526619195c1bdcda5d60ca1b6064820152608401610bbb565b60765481607f54612be5919061569d565b1115612c3d5760405162461bcd60e51b815260206004820152602160248201527f4465706f73697420616d6f756e74206578636565647320414d4d204c502063616044820152600760fc1b6064820152608401610bbb565b6069546000908152606f60209081526040808320338452909152902054158015612c955750606f60006069546001612c75919061569d565b815260208082019290925260409081016000908120338252909252902054155b15612cf657607754811015612cf65760405162461bcd60e51b815260206004820152602160248201527f416d6f756e74206c657373207468616e206d696e4465706f736974416d6f756e6044820152601d60fa1b6064820152608401610bbb565b600160666000828254612d09919061569d565b909155505060665460345460ff1615612d345760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff1615612d575760405162461bcd60e51b8152600401610bbb9061550e565b60006069546001612d68919061569d565b90506000612d7582614a20565b606854909150612d90906001600160a01b03163383886149c6565b607a546001600160a01b0316331415612e085760405162461bcd60e51b815260206004820152603460248201527f43616e2774206465706f736974206469726563746c792061732064656661756c6044820152733a103634b8bab4b234ba3c90383937bb34b232b960611b6064820152608401610bbb565b6069546000908152606f60209081526040808320338452909152902054158015612e4957506000828152606f60209081526040808320338452909152902054155b15612ee25760785460795410612ea15760405162461bcd60e51b815260206004820152601b60248201527f4d617820616d6f756e74206f66207573657273207265616368656400000000006044820152606401610bbb565b6000828152606d602090815260408220805460018181018355918452919092200180546001600160a01b03191633179055607954612ede9161569d565b6079555b6000828152606f6020908152604080832033845290915281208054879290612f0b90849061569d565b909155505060008281526070602052604081208054879290612f2e90849061569d565b9250508190555084607f6000828254612f47919061569d565b9091555050607b546001600160a01b031615612fc257607b546040516302c7739b60e01b8152336004820152602481018790526001600160a01b03909116906302c7739b90604401600060405180830381600087803b158015612fa957600080fd5b505af1158015612fbd573d6000803e3d6000fd5b505050505b606954604080513381526020810188905280820192909252517f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca9181900360600190a150506066548114611cf55760405162461bcd60e51b8152600401610bbb906155ed565b60008080805b6069546000908152607260205260409020548110156131cf57606954600090815260726020526040902080548290811061307857634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015460695483526083825260408084206001600160a01b039092168085529190925291205490925060ff166131bd578192506000836001600160a01b03166337783bfa6040518163ffffffff1660e01b815260040160006040518083038186803b1580156130f157600080fd5b505afa158015613105573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261312d919081019061528e565b5090508080156131ab5750836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561317157600080fd5b505afa158015613185573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131a99190615272565b155b156131bb57600194505050505090565b505b806131c781615737565b91505061302e565b5060009250505090565b6001606660008282546131ec919061569d565b909155505060665460345460ff16156132175760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff161561323a5760405162461bcd60e51b8152600401610bbb9061550e565b6000821161325a5760405162461bcd60e51b8152600401610bbb906155ab565b6069546000908152606c60205260408120546001600160a01b03169080805b6069546000908152607260205260409020548110156135b0578583141561329f576135b0565b60695460009081526072602052604081208054839081106132d057634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015460695483526083825260408084206001600160a01b039092168085529190925291205490915060ff1661359d578092506000836001600160a01b03166337783bfa6040518163ffffffff1660e01b815260040160006040518083038186803b15801561334957600080fd5b505afa15801561335d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613385919081019061528e565b5090508080156134035750836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156133c957600080fd5b505afa1580156133dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134019190615272565b155b1561346e5760675460405163ad385b9160e01b81526001600160a01b0384811660048301526101009092049091169063ad385b9190602401600060405180830381600087803b15801561345557600080fd5b505af1158015613469573d6000803e3d6000fd5b505050505b836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156134a757600080fd5b505afa1580156134bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134df9190615272565b806135565750836001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b15801561351e57600080fd5b505afa158015613532573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135569190615272565b1561359b5760695460009081526083602090815260408083206001600160a01b03861684529091529020805460ff19166001908117909155613598908661569d565b94505b505b50806135a881615737565b915050613279565b5050505060665481146114d35760405162461bcd60e51b8152600401610bbb906155ed565b6135dd614840565b606854600160a01b900460ff16156136425760405162461bcd60e51b815260206004820152602260248201527f4c697175696469747920706f6f6c2068617320616c7265616479207374617274604482015261195960f21b6064820152608401610bbb565b600260005260706020527f60b45511bf7ff7962f4d95774002c49705feb8f9eb913220abee2cd301af1d1d546136ba5760405162461bcd60e51b815260206004820152601d60248201527f63616e206e6f7420737461727420776974682030206465706f736974730000006044820152606401610bbb565b42606b55600260698190556000906136d190614a20565b9050806001600160a01b0316637d3de7ce606b546136ef6002610b3b565b6040516001600160e01b031960e085901b16815260048101929092526024820152604401600060405180830381600087803b15801561372d57600080fd5b505af1158015613741573d6000803e3d6000fd5b50506068805460ff60a01b1916600160a01b17905550506040517f960682678fca98f3ed131eaf165e59544bcd738e948f0b3c64f58fa9e1c65e6090600090a150565b6000606c60006137938461292b565b81526020810191909152604001600020546001600160a01b031692915050565b6137bb614840565b6001600160a01b0381166138035760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610bbb565b600154600160a81b900460ff16156138535760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610bbb565b600080546001600160a01b038381166201000081810262010000600160b01b031990941693909317938490556001805460ff60a81b1916600160a81b1790556040805193909404909116825260208201527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9101610c9e565b60008281526075602081815260408084205460748352818520548686529390925283205490916138fb916156d5565b61390591906156b5565b9392505050565b60345460ff161561392f5760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16156139525760405162461bcd60e51b8152600401610bbb9061550e565b60675461010090046001600160a01b031633146139815760405162461bcd60e51b8152600401610bbb9061549e565b600061398c8361292b565b9050600060018211156139a7576139a282614a20565b6139b4565b607a546001600160a01b03165b6067546068549192506139da916001600160a01b039081169161010090041683866149c6565b60008281526073602090815260408083206001600160a01b038816845290915290205460ff1615613a335760008281526083602090815260408083206001600160a01b03881684529091529020805460ff191660011790555b50505050565b6069546000908152606f602090815260408083206001600160a01b0385168452909152812054151580613ab057506000606f60006069546001613a7c919061569d565b81526020019081526020016000206000846001600160a01b03166001600160a01b0316815260200190815260200160002054115b8015610b6357506001600160a01b03821660009081526071602052604090205460ff161580610b635750506001600160a01b0316600090815260866020526040902054151590565b613b00614840565b60778190556040518181527f990717cc219e5348c1b88bb0ff530d804f0b6f54f3b03844a2bfbe4eb1e9c5d690602001610c9e565b606a54600090610b4c6002846156f4565b613b4e614840565b60768190556040518181527f8c43aa02599ac8f8bab4724621ceea5e7a06b07bbbfaf3b7bd0386cbe481ea3c90602001610c9e565b600160666000828254613b96919061569d565b909155505060665460345460ff1615613bc15760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16613c135760405162461bcd60e51b815260206004820152601a60248201527f526f756e6420636c6f73696e67206e6f742070726570617265640000000000006044820152606401610bbb565b6069546000908152606d602052604090205460855414613c755760405162461bcd60e51b815260206004820152601b60248201527f4e6f7420616c6c2075736572732070726f6365737365642079657400000000006044820152606401610bbb565b6084805460ff191690556069546000908152606c6020908152604080832054606f8352818420607a546001600160a01b039081168652935292205491169015613d6f57606954600090815260746020908152604080832054606f8352818420607a546001600160a01b03168552909252822054670de0b6b3a764000091613cfb916156d5565b613d0591906156b5565b607a54606854919250613d27916001600160a01b0390811691859116846149c6565b607a54604080516001600160a01b039092168252602082018390527fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a910160405180910390a1505b60695460021415613d9d57606954600090815260746020908152604080832054607590925290912055613e00565b606954600081815260746020526040812054670de0b6b3a7640000929091607591613dca906001906156f4565b815260200190815260200160002054613de391906156d5565b613ded91906156b5565b6069546000908152607560205260409020555b606960008154613e0f90615737565b909155506068546040516370a0823160e01b81526001600160a01b038381166004830152909116906370a082319060240160206040518083038186803b158015613e5857600080fd5b505afa158015613e6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e909190615396565b60695460009081526070602052604081208054909190613eb190849061569d565b90915550506069546000818152606f60209081526040808320607a546001600160a01b031684528252808320549383526070909152902054613ef391906156f4565b607f55606954600090613f0590614a20565b6068546040516370a0823160e01b81526001600160a01b038086166004830152929350613fa092859285929116906370a082319060240160206040518083038186803b158015613f5457600080fd5b505afa158015613f68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f8c9190615396565b6068546001600160a01b03169291906149c6565b60006085556069547fc67dda8e11f1aa941c7e74466b1859a07a32f46aaf641d29b83be348424d93cd90613fd6906001906156f4565b607460006001606954613fe991906156f4565b8152602001908152602001600020546040516125fa929190918252602082015260400190565b60845460ff16156140325760405162461bcd60e51b8152600401610bbb9061550e565b6069546000908152606c60205260408120546001600160a01b03169080805b606954600090815260726020526040902054811015613a3357606954600090815260726020526040902080548290811061409b57634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015460695483526083825260408084206001600160a01b039092168085529190925291205490925060ff16614359578192506000836001600160a01b03166337783bfa6040518163ffffffff1660e01b815260040160006040518083038186803b15801561411457600080fd5b505afa158015614128573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052614150919081019061528e565b5090508080156141ce5750836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561419457600080fd5b505afa1580156141a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141cc9190615272565b155b156142395760675460405163ad385b9160e01b81526001600160a01b0385811660048301526101009092049091169063ad385b9190602401600060405180830381600087803b15801561422057600080fd5b505af1158015614234573d6000803e3d6000fd5b505050505b836001600160a01b0316633356a35a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561427257600080fd5b505afa158015614286573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142aa9190615272565b806143215750836001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b1580156142e957600080fd5b505afa1580156142fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143219190615272565b156143575760695460009081526083602090815260408083206001600160a01b03871684529091529020805460ff191660011790555b505b8061436381615737565b915050614051565b60675460ff16156143b45760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b6044820152606401610bbb565b6067805460ff19166001908117909155606655565b606854600090600160a01b900460ff1615806143ee57506143eb606954610b3b565b42105b156143f95750600090565b6000805b60695460009081526072602052604090205481101561451757606954600090815260726020526040812080548390811061444757634e487b7160e01b600052603260045260246000fd5b600091825260208083209091015460695483526083825260408084206001600160a01b039092168085529190925291205490915060ff1661450457809250826001600160a01b031663e990b0986040518163ffffffff1660e01b815260040160206040518083038186803b1580156144be57600080fd5b505afa1580156144d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144f69190615272565b614504576000935050505090565b508061450f81615737565b9150506143fd565b50600191505090565b600160666000828254614533919061569d565b909155505060665460345460ff161561455e5760405162461bcd60e51b8152600401610bbb906154e4565b614566614840565b60005b8281101561474b57600084848381811061459357634e487b7160e01b600052603260045260246000fd5b905060200201359050600181116145ec5760405162461bcd60e51b815260206004820152601e60248201527f43616e27742070756c6c2066726f6d2064656661756c7420726f756e647300006044820152606401610bbb565b606954811061464e5760405162461bcd60e51b815260206004820152602860248201527f43616e27742070756c6c2066726f6d2063757272656e74206f722066757475726044820152676520726f756e647360c01b6064820152608401610bbb565b6000818152606c60205260408082205460685491516370a0823160e01b81526001600160a01b0391821660048201819052939291909116906370a082319060240160206040518083038186803b1580156146a757600080fd5b505afa1580156146bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146df9190615396565b6068549091506146fa906001600160a01b03168333846149c6565b7f772d8b935fea383cb27d439d54ed82e6d7b3f56a51d5f2e1c06d59365cd75d6260695460405161472d91815260200190565b60405180910390a1505050808061474390615737565b915050614569565b506066548114611cf55760405162461bcd60e51b8152600401610bbb906155ed565b60675460009061010090046001600160a01b0316331461479f5760405162461bcd60e51b8152600401610bbb9061549e565b6001606660008282546147b2919061569d565b909155505060665460345460ff16156147dd5760405162461bcd60e51b8152600401610bbb906154e4565b60845460ff16156148005760405162461bcd60e51b8152600401610bbb9061550e565b600061480b8461292b565b905061481681614a20565b925050606654811461483a5760405162461bcd60e51b8152600401610bbb906155ed565b50919050565b6000546201000090046001600160a01b031633146148b85760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610bbb565b565b60345460ff166149035760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610bbb565b6034805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b60345460ff16156149705760405162461bcd60e51b8152600401610bbb906154e4565b6034805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586149303390565b60825460009060ff16156149c257610b6364e8d4a51000836156b5565b5090565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b179052613a33908590614e20565b6000818152606c60205260409020546001600160a01b031680612b5d578160011415614a7b5750607a80546000838152606c6020526040902080546001600160a01b0319166001600160a01b03928316179055905416919050565b607d546001600160a01b0316614ad35760405162461bcd60e51b815260206004820152601d60248201527f526f756e6420706f6f6c206d6173746572636f7079206e6f74207365740000006044820152606401610bbb565b607d54600090614aeb906001600160a01b0316614ef2565b6068549091506001600160a01b038083169163d13f90b49130911686614b156105196001836156f4565b614b1e89610b3b565b6040516001600160e01b031960e088901b1681526001600160a01b03958616600482015294909316602485015260448401919091526064830152608482015260a401600060405180830381600087803b158015614b7a57600080fd5b505af1158015614b8e573d6000803e3d6000fd5b5050506000848152606c602090815260409182902080546001600160a01b0319166001600160a01b03861690811790915582518781529182015292935083927f24c1b21b902a85b5039d7d72427d9376657229eea77880ec9e62031dc950f6ba92500160405180910390a150919050565b607a546001600160a01b0316614c275760405162461bcd60e51b8152600401610bbb90615624565b607a54606854614c45916001600160a01b03918216911684866149c6565b6000818152606f60209081526040808320607a546001600160a01b0316845290915281208054859290614c7990849061569d565b909155505060008181526070602052604081208054859290614c9c90849061569d565b9091555050607a54604080516001600160a01b0390921682526020820185905281018290527f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca9060600160405180910390a1505050565b607a546001600160a01b0316614d1b5760405162461bcd60e51b8152600401610bbb90615624565b607a54606754606854614d45926001600160a01b03918216929082169161010090910416846149c6565b607a546001600160a01b031660009081527f6e075724c97aa8b42d86406682b25042490cf37e8d91270a79e54ad319c8b085602052604081208054839290614d8e90849061569d565b90915550506001600090815260706020527fb1a24bae1e5047fbb0cf526090cbec15c09a4036896111a8964a155b1c4771a18054839290614dd090849061569d565b9091555050607a54604080516001600160a01b039092168252602082018390526001908201527f73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca90606001610c9e565b6000614e75826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614f8a9092919063ffffffff16565b805190915015611cf55780806020019051810190614e939190615272565b611cf55760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bbb565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528260601b60148201526e5af43d82803e903d91602b57fd5bf360881b60288201526037816000f09150506001600160a01b038116612b5d5760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b6044820152606401610bbb565b6060614f998484600085614fa1565b949350505050565b6060824710156150025760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610bbb565b843b6150505760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bbb565b600080866001600160a01b0316858760405161506c9190615421565b60006040518083038185875af1925050503d80600081146150a9576040519150601f19603f3d011682016040523d82523d6000602084013e6150ae565b606091505b50915091506150be8282866150c9565b979650505050505050565b606083156150d8575081613905565b8251156150e85782518084602001fd5b8160405162461bcd60e51b8152600401610bbb919061543d565b8051612b5d81615793565b60006020828403121561511e578081fd5b81356139058161577e565b6000806040838503121561513b578081fd5b82356151468161577e565b946020939093013593505050565b600080600080600080600080610100898b031215615170578384fd5b885161517b8161577e565b8098505060208901519650604089015195506060890151945060808901516151a281615793565b60a08a01519094506151b381615793565b60c08a01519093506151c481615793565b60e08a01519092506151d581615793565b809150509295985092959890939650565b600080602083850312156151f8578182fd5b823567ffffffffffffffff8082111561520f578384fd5b818501915085601f830112615222578384fd5b813581811115615230578485fd5b8660208260051b8501011115615244578485fd5b60209290920196919550909350505050565b600060208284031215615267578081fd5b813561390581615793565b600060208284031215615283578081fd5b815161390581615793565b600080604083850312156152a0578182fd5b82516152ab81615793565b8092505060208084015167ffffffffffffffff808211156152ca578384fd5b818601915086601f8301126152dd578384fd5b8151818111156152ef576152ef615768565b8060051b604051601f19603f8301168101818110858211171561531457615314615768565b604052828152858101935084860182860187018b1015615332578788fd5b8795505b8386101561535b5761534781615102565b855260019590950194938601938601615336565b508096505050505050509250929050565b6000610100828403121561483a578081fd5b60006020828403121561538f578081fd5b5035919050565b6000602082840312156153a7578081fd5b5051919050565b600080604083850312156153c0578182fd5b8235915060208301356153d28161577e565b809150509250929050565b600080604083850312156153ef578182fd5b50508035926020909101359150565b60008060408385031215615410578182fd5b505080516020909101519092909150565b6000825161543381846020870161570b565b9190910192915050565b602081526000825180602084015261545c81604085016020870161570b565b601f01601f19169190910160400192915050565b602080825260149082015273141bdbdb081a185cc81b9bdd081cdd185c9d195960621b604082015260600190565b60208082526026908201527f6f6e6c792074686520414d4d206d617920706572666f726d207468657365206d6040820152656574686f647360d01b606082015260800190565b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b60208082526027908201527f4e6f7420616c6c6f77656420647572696e6720726f756e64436c6f73696e67506040820152661c995c185c995960ca1b606082015260800190565b60208082526036908201527f43616e277420776974686472617720617320796f7520616c72656164792064656040820152751c1bdcda5d195908199bdc881b995e1d081c9bdd5b9960521b606082015260800190565b60208082526022908201527f626174636853697a652068617320746f2062652067726561746572207468616e604082015261020360f41b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526022908201527f64656661756c74206c69717569646974792070726f7669646572206e6f742073604082015261195d60f21b606082015260800190565b6020808252601b908201527f43616e206e6f74207365742061207a65726f2061646472657373210000000000604082015260600190565b600082198211156156b0576156b0615752565b500190565b6000826156d057634e487b7160e01b81526012600452602481fd5b500490565b60008160001904831182151516156156ef576156ef615752565b500290565b60008282101561570657615706615752565b500390565b60005b8381101561572657818101518382015260200161570e565b83811115613a335750506000910152565b600060001982141561574b5761574b615752565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610d1457600080fd5b8015158114610d1457600080fdfea2646970667358221220120375353c99bca4c8e52985ed0a7882dc61743854949eed12750849aed22b1464736f6c63430008040033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.