Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 107555224 | 830 days ago | 0 ETH | ||||
| 107555217 | 830 days ago | 0 ETH | ||||
| 107555185 | 830 days ago | 0 ETH | ||||
| 107555185 | 830 days ago | 0 ETH | ||||
| 107555185 | 830 days ago | 0 ETH | ||||
| 107555185 | 830 days ago | 0 ETH | ||||
| 107555185 | 830 days ago | 0 ETH | ||||
| 107555185 | 830 days ago | 0 ETH | ||||
| 107555185 | 830 days ago | 0 ETH | ||||
| 107555185 | 830 days ago | 0 ETH | ||||
| 107555101 | 830 days ago | 0 ETH | ||||
| 107555101 | 830 days ago | 0 ETH | ||||
| 107555101 | 830 days ago | 0 ETH | ||||
| 107555101 | 830 days ago | 0 ETH | ||||
| 107555101 | 830 days ago | 0 ETH | ||||
| 107555101 | 830 days ago | 0 ETH | ||||
| 107555004 | 830 days ago | 0 ETH | ||||
| 107555004 | 830 days ago | 0 ETH | ||||
| 107555004 | 830 days ago | 0 ETH | ||||
| 107555004 | 830 days ago | 0 ETH | ||||
| 107555004 | 830 days ago | 0 ETH | ||||
| 107555004 | 830 days ago | 0 ETH | ||||
| 107555004 | 830 days ago | 0 ETH | ||||
| 107554934 | 830 days ago | 0 ETH | ||||
| 107554934 | 830 days ago | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
ParlayMarketData
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;
// Inheritance
import "../../interfaces/IParlayMarketsAMM.sol";
import "../../interfaces/ISportPositionalMarket.sol";
import "../../utils/proxy/solidity-0.8.0/ProxyOwned.sol";
import "../../utils/proxy/solidity-0.8.0/ProxyPausable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import "../../utils/libraries/AddressSetLib.sol";
import "./ParlayMarket.sol";
contract ParlayMarketData is Initializable, ProxyOwned, ProxyPausable {
using AddressSetLib for AddressSetLib.AddressSet;
AddressSetLib.AddressSet internal _knownMarkets;
struct ParlayDetails {
uint amount;
uint sUSDPaid;
}
struct SGPFees {
uint tag;
uint sgpMoneylineTotals;
uint sgpMoneylineSpreads;
uint sgpSpreadsTotals;
}
mapping(address => mapping(uint => AddressSetLib.AddressSet)) internal _parlaysInGamePosition;
mapping(address => mapping(uint => mapping(uint => address))) public gameAddressPositionParlay;
mapping(address => mapping(uint => uint)) public numOfParlaysInGamePosition;
mapping(address => ParlayDetails) public parlayDetails;
mapping(address => mapping(uint => address)) public userParlays;
mapping(address => address) public parlayOwner;
mapping(address => uint) public userNumOfParlays;
address public parlayMarketsAMM;
struct ParlayAmmParameters {
uint minUSDAmount;
uint maxSupportedAmount;
uint maxSupportedOdds;
uint parlayAmmFee;
uint safeBoxImpact;
uint parlaySize;
}
function initialize(address _owner, address _parlayMarketsAMM) external initializer {
setOwner(_owner);
parlayMarketsAMM = _parlayMarketsAMM;
}
function getParlayOutcomeDetails(address _parlayMarket)
external
view
returns (
bool initialized,
bool resolved,
bool parlayPaused,
bool alreadyLost,
bool fundsIssued
)
{
ParlayMarket parlay = ParlayMarket(_parlayMarket);
if (parlay.initialized()) {
initialized = parlay.initialized();
resolved = parlay.resolved();
parlayPaused = parlay.paused();
alreadyLost = parlay.parlayAlreadyLost();
fundsIssued = parlay.fundsIssued();
}
}
function getParlayDetails(address _parlayMarket)
external
view
returns (
uint numOfSportMarkets,
uint amount,
uint sUSDPaid,
uint totalResultQuote,
bool resolved,
bool parlayPaused,
bool alreadyLost,
bool fundsIssued,
address[] memory markets,
uint[] memory positions,
uint[] memory oddsOnCreation,
uint[] memory marketResults,
bool[] memory resolvedMarkets,
bool[] memory exercisedMarkets
)
{
ParlayMarket parlay = ParlayMarket(_parlayMarket);
if (parlay.initialized()) {
numOfSportMarkets = parlay.numOfSportMarkets();
amount = parlay.amount();
sUSDPaid = parlay.sUSDPaid();
totalResultQuote = parlay.totalResultQuote();
resolved = parlay.resolved();
parlayPaused = parlay.paused();
alreadyLost = parlay.parlayAlreadyLost();
fundsIssued = parlay.fundsIssued();
markets = new address[](numOfSportMarkets);
positions = new uint[](numOfSportMarkets);
oddsOnCreation = new uint[](numOfSportMarkets);
marketResults = new uint[](numOfSportMarkets);
resolvedMarkets = new bool[](numOfSportMarkets);
exercisedMarkets = new bool[](numOfSportMarkets);
for (uint i = 0; i < numOfSportMarkets; i++) {
(
markets[i],
positions[i],
oddsOnCreation[i],
marketResults[i],
resolvedMarkets[i],
exercisedMarkets[i],
,
) = parlay.sportMarket(i);
}
}
}
function getUserParlays(address _userAccount) external view returns (address[] memory userAllParlays) {
userAllParlays = new address[](userNumOfParlays[_userAccount]);
for (uint i = 0; i < userNumOfParlays[_userAccount]; i++) {
userAllParlays[i] = userParlays[_userAccount][i];
}
}
function getAllParlaysForGamePosition(address _sportMarket, uint _position) external view returns (address[] memory) {
return _getAllParlaysForGamePosition(_sportMarket, _position);
}
function getAllParlaysForGames(address[] memory _sportMarket)
external
view
returns (address[] memory parlays, uint numOfParlays)
{
address[] memory homeParlays;
address[] memory awayParlays;
address[] memory drawParlays;
uint max_length;
uint totalNumOfParlays;
bool addToExercise;
uint8 marketResult;
bool alreadyLost;
for (uint i = 0; i < _sportMarket.length; i++) {
totalNumOfParlays +=
numOfParlaysInGamePosition[_sportMarket[i]][0] +
numOfParlaysInGamePosition[_sportMarket[i]][1] +
numOfParlaysInGamePosition[_sportMarket[i]][2];
}
parlays = new address[](totalNumOfParlays);
for (uint i = 0; i < _sportMarket.length; i++) {
(homeParlays, awayParlays, drawParlays) = _getAllParlaysForGame(_sportMarket[i]);
max_length = homeParlays.length > awayParlays.length ? homeParlays.length : awayParlays.length;
max_length = drawParlays.length > 0 && drawParlays.length > max_length ? drawParlays.length : max_length;
if (ISportPositionalMarket(_sportMarket[i]).resolved() && !ISportPositionalMarket(_sportMarket[i]).cancelled()) {
marketResult = uint8(ISportPositionalMarket(_sportMarket[i]).result());
for (uint j = 0; j < max_length; j++) {
if (homeParlays.length > j) {
alreadyLost = ParlayMarket(homeParlays[j]).parlayAlreadyLost();
addToExercise = ParlayMarket(homeParlays[j]).fundsIssued();
addToExercise =
(!alreadyLost && marketResult != 1) ||
(alreadyLost && marketResult == 1 && !addToExercise);
if (addToExercise) {
parlays[numOfParlays] = homeParlays[j];
numOfParlays++;
}
}
if (awayParlays.length > j) {
alreadyLost = ParlayMarket(awayParlays[j]).parlayAlreadyLost();
addToExercise = ParlayMarket(awayParlays[j]).fundsIssued();
addToExercise =
(!alreadyLost && marketResult != 2) ||
(alreadyLost && marketResult == 2 && !addToExercise);
if (addToExercise) {
parlays[numOfParlays] = awayParlays[j];
numOfParlays++;
}
}
if (drawParlays.length > j) {
alreadyLost = ParlayMarket(drawParlays[j]).parlayAlreadyLost();
addToExercise = ParlayMarket(drawParlays[j]).fundsIssued();
addToExercise =
(!alreadyLost && marketResult != 3) ||
(alreadyLost && marketResult == 3 && !addToExercise);
if (addToExercise) {
parlays[numOfParlays] = drawParlays[j];
numOfParlays++;
}
}
}
}
}
}
function getAllParlaysForGame(address _sportMarket)
external
view
returns (
address[] memory homeParlays,
address[] memory awayParlays,
address[] memory drawParlays
)
{
(homeParlays, awayParlays, drawParlays) = _getAllParlaysForGame(_sportMarket);
}
function getParlayAMMParameters() external view returns (ParlayAmmParameters memory) {
return
ParlayAmmParameters(
IParlayMarketsAMM(parlayMarketsAMM).minUSDAmount(),
IParlayMarketsAMM(parlayMarketsAMM).maxSupportedAmount(),
IParlayMarketsAMM(parlayMarketsAMM).maxSupportedOdds(),
IParlayMarketsAMM(parlayMarketsAMM).parlayAmmFee(),
IParlayMarketsAMM(parlayMarketsAMM).safeBoxImpact(),
IParlayMarketsAMM(parlayMarketsAMM).parlaySize()
);
}
function _getAllParlaysForGame(address _sportMarket)
internal
view
returns (
address[] memory homeParlays,
address[] memory awayParlays,
address[] memory drawParlays
)
{
homeParlays = _getAllParlaysForGamePosition(_sportMarket, 0);
awayParlays = _getAllParlaysForGamePosition(_sportMarket, 1);
if (ISportPositionalMarket(_sportMarket).optionsCount() > 2) {
drawParlays = _getAllParlaysForGamePosition(_sportMarket, 2);
}
}
// todo
function exerciseParlays(address[] memory _parlayMarket) external {
uint profit = IERC20Upgradeable(IParlayMarketsAMM(parlayMarketsAMM).sUSD()).balanceOf(parlayMarketsAMM);
for (uint i = 0; i < _parlayMarket.length; i++) {
if (IParlayMarketsAMM(parlayMarketsAMM).isActiveParlay(_parlayMarket[i])) {
IParlayMarketsAMM(parlayMarketsAMM).exerciseParlay(_parlayMarket[i]);
}
}
profit = IERC20Upgradeable(IParlayMarketsAMM(parlayMarketsAMM).sUSD()).balanceOf(parlayMarketsAMM) - profit;
emit ParlaysExercised(profit, _parlayMarket);
}
// todo
function exerciseSportMarketInParlays(address[] memory _parlayMarket, address _sportMarket) external {
for (uint i = 0; i < _parlayMarket.length; i++) {
if (IParlayMarketsAMM(parlayMarketsAMM).isActiveParlay(_parlayMarket[i])) {
IParlayMarketsAMM(parlayMarketsAMM).exerciseSportMarketInParlay(_parlayMarket[i], _sportMarket);
}
}
}
function addUserParlay(address _parlayMarket, address _parlayOwner) external onlyParlayAMM {
userNumOfParlays[_parlayOwner] = userNumOfParlays[_parlayOwner] + 1;
userParlays[_parlayOwner][userNumOfParlays[_parlayOwner]] = _parlayMarket;
}
function addParlayForGamePosition(
address _game,
uint _position,
address _parlayMarket,
address _parlayOwner
) external onlyParlayAMM {
require(msg.sender == parlayMarketsAMM, "Invalid sender");
if (parlayOwner[_parlayMarket] == address(0)) {
parlayOwner[_parlayMarket] = _parlayOwner;
userNumOfParlays[_parlayOwner] = userNumOfParlays[_parlayOwner] + 1;
userParlays[_parlayOwner][userNumOfParlays[_parlayOwner]] = _parlayMarket;
}
_parlaysInGamePosition[_game][_position].add(_parlayMarket);
gameAddressPositionParlay[_game][_position][numOfParlaysInGamePosition[_game][_position]] = _parlayMarket;
numOfParlaysInGamePosition[_game][_position] += 1;
}
function removeParlayForGamePosition(
address _game,
uint _position,
address _parlayMarket
) external onlyParlayAMM {
require(msg.sender == parlayMarketsAMM, "Invalid sender");
_parlaysInGamePosition[_game][_position].remove(_parlayMarket);
}
function isGameInParlay(address _game, address _parlay) external view returns (bool containsParlay, uint position) {
for (uint i = 0; i < 3; i++) {
if (_parlaysInGamePosition[_game][i].contains(_parlay)) {
containsParlay = true;
position = i;
}
}
}
function isGamePositionInParlay(
address _game,
uint _position,
address _parlay
) public view returns (bool containsParlay) {
containsParlay = _parlaysInGamePosition[_game][_position].contains(_parlay);
}
function getAllSGPFees() external view returns (SGPFees[] memory sgpFees) {
uint numberOfFeesSet;
uint[] memory indexes = new uint[](100);
for (uint i = 9001; i < 9999; i++) {
if (
IParlayMarketsAMM(parlayMarketsAMM).getSgpFeePerCombination(i, 0, 10002) > 0 ||
IParlayMarketsAMM(parlayMarketsAMM).getSgpFeePerCombination(i, 0, 10001) > 0 ||
IParlayMarketsAMM(parlayMarketsAMM).getSgpFeePerCombination(i, 10001, 10002) > 0
) {
indexes[numberOfFeesSet] = i;
++numberOfFeesSet;
}
}
if (numberOfFeesSet > 0) {
sgpFees = new SGPFees[](numberOfFeesSet);
for (uint i = 0; i < numberOfFeesSet; i++) {
sgpFees[i].tag = indexes[i];
sgpFees[i].sgpMoneylineTotals = IParlayMarketsAMM(parlayMarketsAMM).getSgpFeePerCombination(
indexes[i],
0,
10002
);
sgpFees[i].sgpMoneylineSpreads = IParlayMarketsAMM(parlayMarketsAMM).getSgpFeePerCombination(
indexes[i],
0,
10001
);
sgpFees[i].sgpSpreadsTotals = IParlayMarketsAMM(parlayMarketsAMM).getSgpFeePerCombination(
indexes[i],
10001,
10002
);
}
}
}
function getAllSGPFeesForBatch(uint[] calldata tags) external view returns (SGPFees[] memory sgpFees) {
sgpFees = new SGPFees[](tags.length);
for (uint i = 0; i < tags.length; i++) {
sgpFees[i].tag = tags[i];
sgpFees[i].sgpMoneylineTotals = IParlayMarketsAMM(parlayMarketsAMM).getSgpFeePerCombination(tags[i], 0, 10002);
sgpFees[i].sgpMoneylineSpreads = IParlayMarketsAMM(parlayMarketsAMM).getSgpFeePerCombination(tags[i], 0, 10001);
sgpFees[i].sgpSpreadsTotals = IParlayMarketsAMM(parlayMarketsAMM).getSgpFeePerCombination(tags[i], 10001, 10002);
}
}
function _getAllParlaysForGamePosition(address _sportMarket, uint _position)
internal
view
returns (address[] memory allParlays)
{
allParlays = new address[](numOfParlaysInGamePosition[_sportMarket][_position]);
for (uint i = 0; i < numOfParlaysInGamePosition[_sportMarket][_position]; i++) {
allParlays[i] = gameAddressPositionParlay[_sportMarket][_position][i];
}
}
function setParlayMarketsAMM(address _parlayMarketsAMM) external onlyOwner {
parlayMarketsAMM = _parlayMarketsAMM;
emit SetParlayMarketsAMM(_parlayMarketsAMM);
}
modifier onlyParlayAMM() {
_onlyParlayAMM();
_;
}
function _onlyParlayAMM() internal view {
require(msg.sender == parlayMarketsAMM, "Not ParlayAMM");
}
event SetParlayMarketsAMM(address _parlayMarketsAMM);
event ParlaysExercised(uint profit, address[] parlays);
}// 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 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
) 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 exerciseSportMarketInParlay(address _parlayMarket, address _sportMarket) 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 initialMint() external view returns (uint);
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 tags(uint idx) external view returns (uint);
function getTags() external view returns (uint tag1, uint tag2);
function getParentMarketPositions() external view returns (IPosition position1, IPosition position2);
function getStampedOdds()
external
view
returns (
uint,
uint,
uint
);
function balancesOf(address account)
external
view
returns (
uint home,
uint away,
uint draw
);
function totalSupplies()
external
view
returns (
uint home,
uint away,
uint draw
);
function isDoubleChance() external view returns (bool);
function parentMarket() external view returns (ISportPositionalMarket);
/* ========== MUTATIVE FUNCTIONS ========== */
function setPaused(bool _paused) external;
function updateDates(uint256 _maturity, uint256 _expiry) external;
function mint(uint value) external;
function exerciseOptions() external;
function restoreInvalidOdds(
uint _homeOdds,
uint _awayOdds,
uint _drawOdds
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Clone of syntetix contract without constructor
contract ProxyOwned {
address public owner;
address public nominatedOwner;
bool private _initialized;
bool private _transferredAtInit;
function setOwner(address _owner) public {
require(_owner != address(0), "Owner address cannot be 0");
require(!_initialized, "Already initialized, use nominateNewOwner");
_initialized = true;
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
function transferOwnershipAtInit(address proxyAddress) external onlyOwner {
require(proxyAddress != address(0), "Invalid address");
require(!_transferredAtInit, "Already transferred");
owner = proxyAddress;
_transferredAtInit = true;
emit OwnerChanged(owner, proxyAddress);
}
modifier onlyOwner {
_onlyOwner();
_;
}
function _onlyOwner() private view {
require(msg.sender == owner, "Only the contract owner may perform this action");
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 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
// 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 (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
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];
}
}// 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;
uint public numOfResolvedSportMarkets;
uint public numOfAlreadyExercisedSportMarkets;
bool public resolved;
bool public paused;
bool public parlayAlreadyLost;
bool public initialized;
bool public fundsIssued;
mapping(uint => SportMarkets) public sportMarket;
mapping(address => uint) private _sportMarketIndex;
uint private noSkewTotalQuote;
/* ========== 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;
noSkewTotalQuote = (i == 0) ? _marketQuotes[i] : (noSkewTotalQuote * _marketQuotes[i]) / ONE;
}
amount = _amount;
expiry = _expiryDuration;
sUSDPaid = _sUSDPaid;
parlayOwner = _parlayOwner;
totalResultQuote = _totalQuote;
}
function isAnySportMarketExercisable() external view returns (bool isExercisable, address[] memory exercisableMarkets) {
exercisableMarkets = new address[](numOfSportMarkets);
bool exercisable;
for (uint i = 0; i < numOfSportMarkets; i++) {
if (!sportMarket[i].exercised) {
(exercisable, ) = _isWinningSportMarket(sportMarket[i].sportAddress, sportMarket[i].position);
if (exercisable) {
isExercisable = true;
exercisableMarkets[i] = sportMarket[i].sportAddress;
}
}
}
}
//===================== VIEWS ===========================
function isAnySportMarketResolved() external view returns (bool isResolved, address[] memory resolvableMarkets) {
resolvableMarkets = new address[](numOfSportMarkets);
bool resolvable;
for (uint i = 0; i < numOfSportMarkets; i++) {
if (!sportMarket[i].resolved) {
(, resolvable) = _isWinningSportMarket(sportMarket[i].sportAddress, sportMarket[i].position);
if (resolvable) {
isResolved = true;
resolvableMarkets[i] = sportMarket[i].sportAddress;
}
}
}
}
function isUserTheWinner() external view returns (bool finalResult) {
if (resolved) {
finalResult = !parlayAlreadyLost;
} else {
(finalResult, ) = isParlayExercisable();
}
}
function getSportMarketBalances() external view returns (uint[] memory allBalances) {
allBalances = new uint[](numOfSportMarkets);
allBalances = _marketPositionsAndBalances();
}
function phase() public view returns (Phase) {
if (resolved) {
if (resolved && expiry < block.timestamp) {
return Phase.Expiry;
} else {
return Phase.Maturity;
}
} else {
return Phase.Trading;
}
}
function isParlayExercisable() public view returns (bool isExercisable, bool[] memory exercisedOrExercisableMarkets) {
exercisedOrExercisableMarkets = new bool[](numOfSportMarkets);
bool alreadyFalse;
for (uint i = 0; i < numOfSportMarkets; i++) {
if (sportMarket[i].exercised) {
exercisedOrExercisableMarkets[i] = true;
} else if (!sportMarket[i].exercised || !sportMarket[i].resolved) {
(exercisedOrExercisableMarkets[i], ) = _isWinningSportMarket(
sportMarket[i].sportAddress,
sportMarket[i].position
);
}
if (exercisedOrExercisableMarkets[i] == false && !alreadyFalse) {
alreadyFalse = true;
}
}
isExercisable = !alreadyFalse;
}
function getNewResolvedAndWinningPositions()
external
view
returns (bool[] memory newResolvedMarkets, bool[] memory newWinningMarkets)
{
newResolvedMarkets = new bool[](numOfSportMarkets);
newWinningMarkets = new bool[](numOfSportMarkets);
for (uint i = 0; i < numOfSportMarkets; i++) {
if (!sportMarket[i].exercised || !sportMarket[i].resolved) {
(bool exercisable, bool isResolved) = _isWinningSportMarket(
sportMarket[i].sportAddress,
sportMarket[i].position
);
if (isResolved) {
newResolvedMarkets[i] = true;
}
if (exercisable) {
newWinningMarkets[i] = true;
}
}
}
}
//============================== 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");
require(
numOfAlreadyExercisedSportMarkets < numOfSportMarkets && numOfResolvedSportMarkets < numOfSportMarkets,
"Already exercised all markets"
);
for (uint i = 0; i < numOfSportMarkets; i++) {
_updateSportMarketParameters(sportMarket[i].sportAddress, i);
if (sportMarket[i].resolved && !sportMarket[i].exercised) {
_exerciseSpecificSportMarket(sportMarket[i].sportAddress, i);
}
}
if (parlayAlreadyLost && !fundsIssued) {
uint totalSUSDamount = parlayMarketsAMM.sUSD().balanceOf(address(this));
if (totalSUSDamount > 0) {
parlayMarketsAMM.sUSD().transfer(address(parlayMarketsAMM), totalSUSDamount);
}
if (numOfResolvedSportMarkets == numOfSportMarkets) {
fundsIssued = true;
parlayMarketsAMM.resolveParlay();
}
}
}
function exerciseSpecificSportMarket(address _sportMarket) external onlyAMM {
require(_sportMarketIndex[_sportMarket] > 0, "Invalid market");
require(!paused, "Market paused");
uint idx = _sportMarketIndex[_sportMarket] - 1;
_updateSportMarketParameters(_sportMarket, idx);
if (sportMarket[idx].resolved && !sportMarket[idx].exercised) {
_exerciseSpecificSportMarket(_sportMarket, idx);
}
if (parlayAlreadyLost && !fundsIssued) {
uint totalSUSDamount = parlayMarketsAMM.sUSD().balanceOf(address(this));
if (totalSUSDamount > 0) {
parlayMarketsAMM.sUSD().transfer(address(parlayMarketsAMM), totalSUSDamount);
}
if (numOfResolvedSportMarkets == numOfSportMarkets) {
fundsIssued = true;
parlayMarketsAMM.resolveParlay();
}
}
}
//============================== INTERNAL FUNCTIONS ===================================
function _exerciseSpecificSportMarket(address _sportMarket, uint _idx) internal {
require(!sportMarket[_idx].exercised, "Exercised");
require(sportMarket[_idx].resolved, "Unresolved");
bool exercizable = sportMarket[_idx].resolved &&
(sportMarket[_idx].hasWon || sportMarket[_idx].isCancelled) &&
!sportMarket[_idx].exercised
? true
: false;
if (exercizable) {
ISportPositionalMarket(_sportMarket).exerciseOptions();
sportMarket[_idx].exercised = true;
numOfAlreadyExercisedSportMarkets++;
if (
numOfResolvedSportMarkets == numOfSportMarkets &&
numOfAlreadyExercisedSportMarkets == numOfSportMarkets &&
!parlayAlreadyLost
) {
uint totalSUSDamount = parlayMarketsAMM.sUSD().balanceOf(address(this));
uint calculatedAmount = _recalculateAmount();
_resolve(true);
if (phase() != Phase.Expiry) {
if (calculatedAmount < totalSUSDamount) {
parlayMarketsAMM.sUSD().transfer(parlayOwner, calculatedAmount);
parlayMarketsAMM.sUSD().transfer(address(parlayMarketsAMM), (totalSUSDamount - calculatedAmount));
} else {
parlayMarketsAMM.sUSD().transfer(parlayOwner, totalSUSDamount);
}
fundsIssued = true;
parlayMarketsAMM.resolveParlay();
}
}
} else {
if (!parlayAlreadyLost) {
_resolve(false);
}
}
}
function _updateSportMarketParameters(address _sportMarket, uint _idx) internal {
if (!sportMarket[_idx].resolved) {
ISportPositionalMarket currentSportMarket = ISportPositionalMarket(_sportMarket);
uint result = uint(currentSportMarket.result());
bool isResolved = currentSportMarket.resolved();
if (isResolved) {
numOfResolvedSportMarkets = numOfResolvedSportMarkets + 1;
sportMarket[_idx].resolved = isResolved;
sportMarket[_idx].result = result;
sportMarket[_idx].hasWon = result == (sportMarket[_idx].position + 1);
if (result == 0) {
(totalResultQuote, noSkewTotalQuote) = _getCancellQuotesForMarketIndex(_idx);
sportMarket[_idx].isCancelled = true;
}
}
}
}
function _marketPositionsAndBalances() internal view returns (uint[] memory balances) {
uint[] memory allBalancesPerMarket = new uint[](3);
balances = new uint[](numOfSportMarkets);
for (uint i = 0; i < numOfSportMarkets; i++) {
(allBalancesPerMarket[0], allBalancesPerMarket[1], allBalancesPerMarket[2]) = ISportPositionalMarket(
sportMarket[i].sportAddress
).balancesOf(address(this));
balances[i] = allBalancesPerMarket[sportMarket[i].position];
}
}
function _recalculateAmount() internal view returns (uint recalculated) {
recalculated = ((sUSDPaid * ONE * ONE) / totalResultQuote) / ONE;
}
function _resolve(bool _userWon) internal {
parlayAlreadyLost = !_userWon;
resolved = true;
parlayMarketsAMM.triggerResolvedEvent(parlayOwner, _userWon);
emit Resolved(_userWon);
}
function _isWinningSportMarket(address _sportMarket, uint _userPosition)
internal
view
returns (bool isWinning, bool isResolved)
{
ISportPositionalMarket currentSportMarket = ISportPositionalMarket(_sportMarket);
if (currentSportMarket.resolved()) {
isResolved = true;
}
if (
isResolved &&
(uint(currentSportMarket.result()) == (_userPosition + 1) ||
currentSportMarket.result() == ISportPositionalMarket.Side.Cancelled)
) {
isWinning = true;
}
}
function _getCancellQuotesForMarketIndex(uint _index) internal view returns (uint discountedQuote, uint newNoSkewQuote) {
newNoSkewQuote = ((noSkewTotalQuote * ONE * ONE) / sportMarket[_index].odd) / ONE;
discountedQuote =
totalResultQuote +
(((ONE - totalResultQuote) * (newNoSkewQuote - noSkewTotalQuote)) / (ONE - noSkewTotalQuote));
}
//============================== 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");
emit Expired(beneficiary);
_selfDestruct(beneficiary);
}
function _selfDestruct(address payable beneficiary) internal {
// Transfer the balance rather than the deposit value in case there are any synths left over
// from direct transfers.
for (uint i = 0; i < numOfSportMarkets; i++) {
_updateSportMarketParameters(sportMarket[i].sportAddress, i);
if (sportMarket[i].resolved && !sportMarket[i].exercised) {
_exerciseSpecificSportMarket(sportMarket[i].sportAddress, i);
}
}
uint balance = parlayMarketsAMM.sUSD().balanceOf(address(this));
if (balance != 0) {
parlayMarketsAMM.sUSD().transfer(beneficiary, balance);
fundsIssued = true;
}
// 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;
// interfaces
import "./ParlayMarket.sol";
import "../../interfaces/IParlayMarketsAMM.sol";
import "../../interfaces/ISportsAMM.sol";
import "../../interfaces/IParlayMarketData.sol";
import "../../interfaces/ISportPositionalMarket.sol";
import "../../interfaces/ISportPositionalMarketManager.sol";
import "../../interfaces/IStakingThales.sol";
import "../../interfaces/IReferrals.sol";
import "../../interfaces/ICurveSUSD.sol";
import "../../interfaces/ITherundownConsumer.sol";
contract ParlayVerifier {
uint private constant ONE = 1e18;
uint private constant TAG_F1 = 9445;
uint private constant TAG_MOTOGP = 9497;
uint private constant TAG_NUMBER_SPREAD = 10001;
uint private constant TAG_NUMBER_TOTAL = 10002;
uint private constant DOUBLE_CHANCE_TAG = 10003;
struct InitialQuoteParameters {
address[] sportMarkets;
uint[] positions;
uint totalSUSDToPay;
uint parlaySize;
uint defaultONE;
uint sgpFee;
ISportsAMM sportsAMM;
address parlayAMM;
}
struct FinalQuoteParameters {
address[] sportMarkets;
uint[] positions;
uint[] buyQuoteAmounts;
ISportsAMM sportsAmm;
uint sUSDAfterFees;
uint defaultONE;
uint sgpFee;
}
struct VerifyMarket {
address[] sportMarkets;
ISportsAMM sportsAMM;
address parlayAMM;
}
struct CachedMarket {
bytes32 gameId;
uint gameCounter;
uint tag1;
uint tag2;
}
// ISportsAMM sportsAmm;
function _verifyMarkets(VerifyMarket memory params)
internal
view
returns (
// address[] memory _sportMarkets,
// uint[] memory _positions,
// uint _totalSUSDToPay,
// ISportsAMM _sportsAMM,
// address _parlayAMM
bool eligible,
uint sgpFee
)
{
eligible = true;
ITherundownConsumer consumer = ITherundownConsumer(params.sportsAMM.theRundownConsumer());
// bytes32[] memory cachedTeams = new bytes32[](_sportMarkets.length * 2);
CachedMarket[] memory cachedTeams = new CachedMarket[](params.sportMarkets.length * 2);
uint lastCachedIdx = 0;
bytes32 gameIdHome;
bytes32 gameIdAway;
uint tag1;
uint tag2;
uint motoCounter = 0;
for (uint i = 0; i < params.sportMarkets.length; i++) {
address sportMarket = params.sportMarkets[i];
(gameIdHome, gameIdAway) = _getGameIds(consumer, sportMarket);
tag1 = ISportPositionalMarket(sportMarket).tags(0);
tag2 = consumer.isChildMarket(sportMarket) ? ISportPositionalMarket(sportMarket).tags(1) : 0;
motoCounter = (tag1 == TAG_F1 || tag1 == TAG_MOTOGP) ? ++motoCounter : motoCounter;
require(motoCounter <= 1, "2xMotosport");
// check if game IDs already exist
for (uint j = 0; j < lastCachedIdx; j++) {
if (
(cachedTeams[j].gameId == gameIdHome ||
(j > 1 && cachedTeams[j].gameId == gameIdAway && cachedTeams[j - 1].gameId != gameIdHome)) &&
cachedTeams[j].tag1 == tag1
) {
uint feeToApply = IParlayMarketsAMM(params.parlayAMM).getSgpFeePerCombination(
tag1,
tag2,
cachedTeams[j].tag2
);
if (cachedTeams[j].gameCounter > 0 || feeToApply == 0) {
revert("SameTeamOnParlay");
}
cachedTeams[j].gameCounter += 1;
sgpFee = sgpFee > 0 ? (sgpFee * feeToApply) / ONE : feeToApply;
}
}
(cachedTeams[lastCachedIdx].tag1, cachedTeams[lastCachedIdx].tag2) = (tag1, tag2);
cachedTeams[lastCachedIdx++].gameId = gameIdHome;
(cachedTeams[lastCachedIdx].tag1, cachedTeams[lastCachedIdx].tag2) = (tag1, tag2);
cachedTeams[lastCachedIdx++].gameId = gameIdAway;
}
}
function _calculateRisk(
address[] memory _sportMarkets,
uint _sUSDInRisky,
address _parlayAMM
) internal view returns (bool riskFree) {
// address[] memory sortedAddresses = new address[](_sportMarkets.length);
// sortedAddresses = _sort(_sportMarkets);
require(_checkRisk(_sportMarkets, _sUSDInRisky, _parlayAMM), "RiskPerComb exceeded");
riskFree = true;
}
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;
(eligible, params.sgpFee) = _verifyMarkets(VerifyMarket(params.sportMarkets, params.sportsAMM, params.parlayAMM));
if (eligible && numOfMarkets == params.positions.length && numOfMarkets > 0 && numOfMarkets <= params.parlaySize) {
finalQuotes = new uint[](numOfMarkets);
amountsToBuy = new uint[](numOfMarkets);
uint[] memory marketOdds;
for (uint i = 0; i < numOfMarkets; i++) {
if (params.positions[i] > 2) {
totalQuote = 0;
break;
}
marketOdds = params.sportsAMM.getMarketDefaultOdds(params.sportMarkets[i], false);
if (marketOdds.length == 0) {
totalQuote = 0;
break;
}
finalQuotes[i] = (params.defaultONE * marketOdds[params.positions[i]]);
totalQuote = totalQuote == 0 ? finalQuotes[i] : (totalQuote * finalQuotes[i]) / ONE;
skewImpact = skewImpact + finalQuotes[i];
// use as inverseQuotes
finalQuotes[i] = ONE - finalQuotes[i];
inverseSum = inverseSum + finalQuotes[i];
if (totalQuote == 0) {
totalQuote = 0;
break;
}
}
if (totalQuote > 0) {
for (uint i = 0; i < finalQuotes.length; i++) {
// use finalQuotes as inverseQuotes in equation
// skewImpact is sumOfQuotes
// inverseSum is sum of InverseQuotes
amountsToBuy[i] =
((ONE * finalQuotes[i] * params.totalSUSDToPay * skewImpact)) /
(totalQuote * inverseSum * skewImpact);
}
(totalQuote, totalBuyAmount, skewImpact, finalQuotes, amountsToBuy) = calculateFinalQuotes(
FinalQuoteParameters(
params.sportMarkets,
params.positions,
amountsToBuy,
params.sportsAMM,
params.totalSUSDToPay,
params.defaultONE,
params.sgpFee
)
);
}
}
}
function calculateFinalQuotes(FinalQuoteParameters memory params)
internal
view
returns (
uint totalQuote,
uint totalBuyAmount,
uint skewImpact,
uint[] memory finalQuotes,
uint[] memory buyAmountPerMarket
)
{
uint[] memory buyQuoteAmountPerMarket = new uint[](params.sportMarkets.length);
buyAmountPerMarket = params.buyQuoteAmounts;
finalQuotes = new uint[](params.sportMarkets.length);
for (uint i = 0; i < params.sportMarkets.length; i++) {
totalBuyAmount += params.buyQuoteAmounts[i];
// buyQuote always calculated with added SportsAMM fees
buyQuoteAmountPerMarket[i] = (params.defaultONE *
params.sportsAmm.buyFromAmmQuote(
params.sportMarkets[i],
obtainSportsAMMPosition(params.positions[i]),
params.buyQuoteAmounts[i]
));
if (buyQuoteAmountPerMarket[i] == 0) {
totalQuote = 0;
totalBuyAmount = 0;
}
}
for (uint i = 0; i < params.sportMarkets.length; i++) {
finalQuotes[i] = ((buyQuoteAmountPerMarket[i] * ONE * ONE) / params.buyQuoteAmounts[i]) / ONE;
totalQuote = (i == 0) ? finalQuotes[i] : (totalQuote * finalQuotes[i]) / ONE;
}
if (totalQuote > 0) {
totalQuote = params.sgpFee > 0 ? ((totalQuote * ONE * ONE) / params.sgpFee) / ONE : totalQuote;
if (totalQuote < IParlayMarketsAMM(params.sportsAmm.parlayAMM()).maxSupportedOdds()) {
totalQuote = IParlayMarketsAMM(params.sportsAmm.parlayAMM()).maxSupportedOdds();
}
uint expectedPayout = ((params.sUSDAfterFees * ONE * ONE) / totalQuote) / ONE;
skewImpact = expectedPayout > totalBuyAmount
? (((ONE * expectedPayout) - (ONE * totalBuyAmount)) / (totalBuyAmount))
: (((ONE * totalBuyAmount) - (ONE * expectedPayout)) / (totalBuyAmount));
buyAmountPerMarket = _applySkewImpactBatch(buyAmountPerMarket, skewImpact, (expectedPayout > totalBuyAmount));
// finalQuotes = _applySkewImpactBatch(finalQuotes, (ONE*skewImpact/(4*finalQuotes.length))/ONE, (expectedPayout > totalBuyAmount));
totalBuyAmount = applySkewImpact(totalBuyAmount, skewImpact, (expectedPayout > totalBuyAmount));
_calculateRisk(params.sportMarkets, (totalBuyAmount - params.sUSDAfterFees), params.sportsAmm.parlayAMM());
} else {
totalBuyAmount = 0;
}
}
function applySkewImpact(
uint _value,
uint _skewImpact,
bool _addition
) public pure returns (uint newValue) {
newValue = _addition ? (((ONE + _skewImpact) * _value) / ONE) : (((ONE - _skewImpact) * _value) / ONE);
}
function _applySkewImpactBatch(
uint[] memory _values,
uint _skewImpact,
bool _addition
) internal pure returns (uint[] memory newValues) {
newValues = new uint[](_values.length);
for (uint i = 0; i < _values.length; i++) {
newValues[i] = applySkewImpact(_values[i], _skewImpact, _addition);
}
}
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 calculateCombinationKey(address[] memory _sportMarkets) public pure returns (bytes32) {
address[] memory sortedAddresses = new address[](_sportMarkets.length);
sortedAddresses = _sort(_sportMarkets);
return keccak256(abi.encodePacked(sortedAddresses));
}
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 sort(address[] memory data) external pure returns (address[] memory) {
_quickSort(data, int(0), int(data.length - 1));
return data;
}
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 _getGameIds(ITherundownConsumer consumer, address sportMarket)
internal
view
returns (bytes32 home, bytes32 away)
{
ITherundownConsumer.GameCreate memory game = consumer.getGameCreatedById(consumer.gameIdPerMarket(sportMarket));
home = keccak256(abi.encodePacked(game.homeTeam));
away = keccak256(abi.encodePacked(game.awayTeam));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
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 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 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 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);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IParlayMarketData {
/* ========== VIEWS / VARIABLES ========== */
function hasParlayGamePosition(
address _parlay,
address _game,
uint _position
) external view returns (bool containsParlay);
function addParlayForGamePosition(
address _game,
uint _position,
address _parlayMarket,
address _parlayOwner
) external;
function removeParlayForGamePosition(
address _game,
uint _position,
address _parlayMarket
) external;
}// 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 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;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
interface IStakingThales {
function updateVolume(address account, uint amount) 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.5.16;
interface IReferrals {
function referrals(address) external view returns (address);
function sportReferrals(address) external view returns (address);
function setReferrer(address, address) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
interface ICurveSUSD {
function exchange_underlying(
int128 i,
int128 j,
uint256 _dx,
uint256 _min_dy
) external returns (uint256);
function get_dy_underlying(
int128 i,
int128 j,
uint256 _dx
) external view returns (uint256);
// @notice Perform an exchange between two underlying coins
// @param i Index value for the underlying coin to send
// @param j Index valie of the underlying coin to receive
// @param _dx Amount of `i` being exchanged
// @param _min_dy Minimum amount of `j` to receive
// @param _receiver Address that receives `j`
// @return Actual amount of `j` received
// indexes:
// 0 = sUSD 18 dec 0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9
// 1= DAI 18 dec 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1
// 2= USDC 6 dec 0x7F5c764cBc14f9669B88837ca1490cCa17c31607
// 3= USDT 6 dec 0x94b008aA00579c1307B0EF2c499aD98a8ce58e58
}// 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 getNormalizedOdds(bytes32 _gameId) external view returns (uint[] memory);
function getNormalizedOddsForMarket(address _market) external view returns (uint[] memory);
function getNormalizedChildOdds(address _market) external view returns (uint[] memory);
function getNormalizedOddsForTwoPosition(bytes32 _gameId) 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);
// 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 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 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);
}// 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
// 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
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
// 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";
// 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;
IERC20 sUSD;
address creator;
bytes32 gameId;
string gameLabel;
uint[2] times; // [maturity, expiry]
uint deposit; // sUSD deposit
address theRundownConsumer;
address sportsAMM;
uint positionCount;
address[] positions;
uint[] tags;
bool isChild;
address parentMarket;
bool isDoubleChance;
}
/* ========== STATE VARIABLES ========== */
Options public options;
uint public override optionsCount;
Times public override times;
GameDetails public gameDetails;
ITherundownConsumer public theRundownConsumer;
IERC20 public sUSD;
address public sportsAMM;
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;
uint public override initialMint;
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;
/* ========== CONSTRUCTOR ========== */
function initialize(SportPositionalMarketParameters calldata _parameters) external {
require(!initialized, "Positional Market already initialized");
initialized = true;
initOwner(_parameters.owner);
sUSD = _parameters.sUSD;
creator = _parameters.creator;
theRundownConsumer = ITherundownConsumer(_parameters.theRundownConsumer);
gameDetails = GameDetails(_parameters.gameId, _parameters.gameLabel);
tags = _parameters.tags;
times = Times(_parameters.times[0], _parameters.times[1]);
deposited = _parameters.deposit;
initialMint = _parameters.deposit;
optionsCount = _parameters.positionCount;
sportsAMM = _parameters.sportsAMM;
isDoubleChance = _parameters.isDoubleChance;
parentMarket = ISportPositionalMarket(_parameters.parentMarket);
require(optionsCount == _parameters.positions.length, "Position count mismatch");
// Instantiate the options themselves
options.home = SportPosition(_parameters.positions[0]);
options.away = SportPosition(_parameters.positions[1]);
// abi.encodePacked("sUP: ", _oracleKey)
// consider naming the option: sUpBTC>[email protected]
if (_parameters.isChild) {
isChild = true;
require(tags.length > 1, "Child markets must have two tags");
if (tags[1] == 10001) {
options.home.initialize(gameDetails.gameLabel, "HOME", _parameters.sportsAMM);
options.away.initialize(gameDetails.gameLabel, "AWAY", _parameters.sportsAMM);
} else if (tags[1] == 10002) {
options.home.initialize(gameDetails.gameLabel, "OVER", _parameters.sportsAMM);
options.away.initialize(gameDetails.gameLabel, "UNDER", _parameters.sportsAMM);
}
} else {
options.home.initialize(gameDetails.gameLabel, "HOME", _parameters.sportsAMM);
options.away.initialize(gameDetails.gameLabel, "AWAY", _parameters.sportsAMM);
}
if (optionsCount > 2) {
options.draw = SportPosition(_parameters.positions[2]);
options.draw.initialize(gameDetails.gameLabel, "DRAW", _parameters.sportsAMM);
}
if (initialMint > 0) {
_mint(creator, initialMint);
}
// Note: the ERC20 base contract does not have a constructor, so we do not have to worry
// about initializing its state separately
}
/* ---------- External Contracts ---------- */
function _manager() internal view returns (SportPositionalMarketManager) {
return SportPositionalMarketManager(owner);
}
/* ---------- Phases ---------- */
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 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");
times = 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 (gameDetails.gameId, gameDetails.gameLabel);
}
function getParentMarketPositions() public view override returns (IPosition position1, IPosition position2) {
if (isDoubleChance) {
(IPosition home, IPosition away, IPosition draw) = parentMarket.getOptions();
if (keccak256(abi.encodePacked(gameDetails.gameLabel)) == keccak256(abi.encodePacked("HomeTeamNotToLose"))) {
(position1, position2) = (home, draw);
} else if (
keccak256(abi.encodePacked(gameDetails.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 gameDetails.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();
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 (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;
}
/* ---------- 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 == 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 (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);
}
}
}
/* ---------- Custom oracle configuration ---------- */
function setTherundownConsumer(address _theRundownConsumer) external onlyOwner {
theRundownConsumer = ITherundownConsumer(_theRundownConsumer);
emit SetTherundownConsumer(_theRundownConsumer);
}
function setsUSD(address _address) external onlyOwner {
sUSD = IERC20(_address);
emit SetsUSD(_address);
}
/* ---------- 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(theRundownConsumer).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);
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 = sUSD.balanceOf(address(this));
if (balance != 0) {
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 SetsUSD(address _address);
event SetTherundownConsumer(address _address);
event Expired(address beneficiary);
event StoredOddsOnCancellation(uint homeOdds, uint awayOdds, uint drawOdds);
event PauseUpdated(bool _paused);
event DatesUpdated(uint256 _maturity, uint256 _expiry);
}// 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.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;
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.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";
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 public 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 doesSportSupportDoubleChance;
/* ========== 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 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) {
if (doubleChanceMarketsByParent[market].length > 0) {
address[] memory markets = new address[](3);
for (uint i = 0; i < doubleChanceMarketsByParent[market].length; i++) {
markets[i] = doubleChanceMarketsByParent[market][i];
}
return markets;
}
}
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 ||
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 || msg.sender == oddsObtainer, "Invalid caller");
uint expiry = _newStartTime.add(expiryDuration);
// update main market
_updateDatesForMarket(_market, _newStartTime, expiry);
// number of child
uint numberOfChildMarkets = IGamesOddsObtainer(oddsObtainer).numberOfChildMarkets(_market);
for (uint i = 0; i < numberOfChildMarkets; i++) {
address child = IGamesOddsObtainer(oddsObtainer).mainMarketChildMarketIndex(_market, i);
_updateDatesForMarket(child, _newStartTime, expiry);
}
}
function isMarketPaused(address _market) external view override returns (bool) {
return ISportPositionalMarket(_market).paused();
}
/* ========== 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, "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,
sUSD,
gameId,
gameLabel,
[maturity, expiry],
initialMint,
positionCount,
msg.sender,
tags,
isChild,
parentMarket,
false
)
);
// The debt can't be incremented in the new market's constructor because until construction is complete,
// the manager doesn't know its address in order to grant it permission.
totalDeposited = totalDeposited.add(initialMint);
sUSD.transferFrom(msg.sender, address(market), _transformCollateral(initialMint));
if (positionCount > 2 && isDoubleChanceSupported) {
_createDoubleChanceMarkets(msg.sender, gameId, maturity, expiry, initialMint, 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,
marketContract.initialMint(),
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,
uint initialMint,
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,
sUSD,
gameId,
labels[i],
[maturity, expiry],
initialMint,
2,
creator,
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 ||
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 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 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 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);
}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;
IERC20 _sUSD;
bytes32 gameId;
string gameLabel;
uint[2] times; // [maturity, expiry]
uint initialMint;
uint positionCount;
address theRundownConsumer;
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);
for (uint i = 0; i < _parameters.positionCount; i++) {
positions[i] = address(SportPosition(Clones.clone(positionMastercopy)));
}
pom.initialize(
SportPositionalMarket.SportPositionalMarketParameters(
positionalMarketManager,
_parameters._sUSD,
_parameters.creator,
_parameters.gameId,
_parameters.gameLabel,
_parameters.times,
_parameters.initialMint,
_parameters.theRundownConsumer,
sportsAMM,
_parameters.positionCount,
positions,
_parameters.tags,
_parameters.isChild,
_parameters.parentMarket,
_parameters.isDoubleChance
)
);
emit MarketCreated(
address(pom),
_parameters.gameId,
_parameters.gameLabel,
_parameters.times[0],
_parameters.times[1],
_parameters.initialMint,
_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) external view returns (bool);
function invalidOdds(address _market) external view returns (bool);
function getNormalizedOdds(bytes32 _gameId) external view returns (uint[] memory);
function getNormalizedChildOdds(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 currentActiveTotalChildMarket(address _main) external view returns (address);
function currentActiveSpreadChildMarket(address _main) external view returns (address);
function isSpreadChildMarket(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
) external;
function setFirstOdds(
bytes32 _gameId,
int24 _homeOdds,
int24 _awayOdds,
int24 _drawOdds
) external;
function setFirstNormalizedOdds(bytes32 _gameId, address _market) 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
// 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
// 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);
}
}
}
}{
"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":"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":"uint256","name":"profit","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"parlays","type":"address[]"}],"name":"ParlaysExercised","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_parlayMarketsAMM","type":"address"}],"name":"SetParlayMarketsAMM","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_game","type":"address"},{"internalType":"uint256","name":"_position","type":"uint256"},{"internalType":"address","name":"_parlayMarket","type":"address"},{"internalType":"address","name":"_parlayOwner","type":"address"}],"name":"addParlayForGamePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_parlayMarket","type":"address"},{"internalType":"address","name":"_parlayOwner","type":"address"}],"name":"addUserParlay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_parlayMarket","type":"address[]"}],"name":"exerciseParlays","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_parlayMarket","type":"address[]"},{"internalType":"address","name":"_sportMarket","type":"address"}],"name":"exerciseSportMarketInParlays","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"gameAddressPositionParlay","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_sportMarket","type":"address"}],"name":"getAllParlaysForGame","outputs":[{"internalType":"address[]","name":"homeParlays","type":"address[]"},{"internalType":"address[]","name":"awayParlays","type":"address[]"},{"internalType":"address[]","name":"drawParlays","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_sportMarket","type":"address"},{"internalType":"uint256","name":"_position","type":"uint256"}],"name":"getAllParlaysForGamePosition","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_sportMarket","type":"address[]"}],"name":"getAllParlaysForGames","outputs":[{"internalType":"address[]","name":"parlays","type":"address[]"},{"internalType":"uint256","name":"numOfParlays","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllSGPFees","outputs":[{"components":[{"internalType":"uint256","name":"tag","type":"uint256"},{"internalType":"uint256","name":"sgpMoneylineTotals","type":"uint256"},{"internalType":"uint256","name":"sgpMoneylineSpreads","type":"uint256"},{"internalType":"uint256","name":"sgpSpreadsTotals","type":"uint256"}],"internalType":"struct ParlayMarketData.SGPFees[]","name":"sgpFees","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tags","type":"uint256[]"}],"name":"getAllSGPFeesForBatch","outputs":[{"components":[{"internalType":"uint256","name":"tag","type":"uint256"},{"internalType":"uint256","name":"sgpMoneylineTotals","type":"uint256"},{"internalType":"uint256","name":"sgpMoneylineSpreads","type":"uint256"},{"internalType":"uint256","name":"sgpSpreadsTotals","type":"uint256"}],"internalType":"struct ParlayMarketData.SGPFees[]","name":"sgpFees","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getParlayAMMParameters","outputs":[{"components":[{"internalType":"uint256","name":"minUSDAmount","type":"uint256"},{"internalType":"uint256","name":"maxSupportedAmount","type":"uint256"},{"internalType":"uint256","name":"maxSupportedOdds","type":"uint256"},{"internalType":"uint256","name":"parlayAmmFee","type":"uint256"},{"internalType":"uint256","name":"safeBoxImpact","type":"uint256"},{"internalType":"uint256","name":"parlaySize","type":"uint256"}],"internalType":"struct ParlayMarketData.ParlayAmmParameters","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_parlayMarket","type":"address"}],"name":"getParlayDetails","outputs":[{"internalType":"uint256","name":"numOfSportMarkets","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"sUSDPaid","type":"uint256"},{"internalType":"uint256","name":"totalResultQuote","type":"uint256"},{"internalType":"bool","name":"resolved","type":"bool"},{"internalType":"bool","name":"parlayPaused","type":"bool"},{"internalType":"bool","name":"alreadyLost","type":"bool"},{"internalType":"bool","name":"fundsIssued","type":"bool"},{"internalType":"address[]","name":"markets","type":"address[]"},{"internalType":"uint256[]","name":"positions","type":"uint256[]"},{"internalType":"uint256[]","name":"oddsOnCreation","type":"uint256[]"},{"internalType":"uint256[]","name":"marketResults","type":"uint256[]"},{"internalType":"bool[]","name":"resolvedMarkets","type":"bool[]"},{"internalType":"bool[]","name":"exercisedMarkets","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_parlayMarket","type":"address"}],"name":"getParlayOutcomeDetails","outputs":[{"internalType":"bool","name":"initialized","type":"bool"},{"internalType":"bool","name":"resolved","type":"bool"},{"internalType":"bool","name":"parlayPaused","type":"bool"},{"internalType":"bool","name":"alreadyLost","type":"bool"},{"internalType":"bool","name":"fundsIssued","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_userAccount","type":"address"}],"name":"getUserParlays","outputs":[{"internalType":"address[]","name":"userAllParlays","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_parlayMarketsAMM","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_game","type":"address"},{"internalType":"address","name":"_parlay","type":"address"}],"name":"isGameInParlay","outputs":[{"internalType":"bool","name":"containsParlay","type":"bool"},{"internalType":"uint256","name":"position","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_game","type":"address"},{"internalType":"uint256","name":"_position","type":"uint256"},{"internalType":"address","name":"_parlay","type":"address"}],"name":"isGamePositionInParlay","outputs":[{"internalType":"bool","name":"containsParlay","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastPauseTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"numOfParlaysInGamePosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"parlayDetails","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"sUSDPaid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"parlayMarketsAMM","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"parlayOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_game","type":"address"},{"internalType":"uint256","name":"_position","type":"uint256"},{"internalType":"address","name":"_parlayMarket","type":"address"}],"name":"removeParlayForGamePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_parlayMarketsAMM","type":"address"}],"name":"setParlayMarketsAMM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAddress","type":"address"}],"name":"transferOwnershipAtInit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userNumOfParlays","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userParlays","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b5061431b806100206000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c806379ba50971161011a578063c3b83f5f116100ad578063e1d4746a1161007c578063e1d4746a14610563578063e58100e41461059d578063e5bf48fc146105b0578063f5687198146105d2578063febec2a71461062657600080fd5b8063c3b83f5f146104fd578063c9eb8c9214610510578063cc39a1d71461053d578063cd4203c31461055057600080fd5b80638e1dadb4116100e95780638e1dadb4146104bb57806391b4ded9146104ce578063a2c94618146104d7578063be583ec7146104ea57600080fd5b806379ba50971461045057806384084933146104585780638d4ea476146104785780638da5cb5b146104a257600080fd5b80632f47a0f31161019d578063485cc9551161016c578063485cc955146103f757806353a47bb71461040a5780635c975abb1461041d5780636853366d1461042a5780636d9d3d461461043d57600080fd5b80632f47a0f314610349578063311a3b731461036a57806337a897d01461039e578063467979aa146103d757600080fd5b806316c38b3c116101d957806316c38b3c146102745780631955003d146102875780631a3dcf2a146102c857806323a831291461030d57600080fd5b80630b9ffc4e1461020b57806313af403514610229578063159fe4211461023e5780631627540c14610261575b600080fd5b610213610639565b60405161022091906140fe565b60405180910390f35b61023c610237366004613c94565b610c47565b005b61025161024c366004613d1a565b610d87565b6040519015158152602001610220565b61023c61026f366004613c94565b610dbc565b61023c610282366004613f58565b610e12565b6102b0610295366004613c94565b600b602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610220565b6102db6102d6366004613c94565b610e88565b60408051951515865293151560208601529115159284019290925290151560608301521515608082015260a001610220565b61033461031b366004613c94565b6009602052600090815260409020805460019091015482565b60408051928352602083019190915201610220565b61035c610357366004613e73565b611154565b6040516102209291906140dc565b6102b0610378366004613cef565b600a6020908152600092835260408084209091529082529020546001600160a01b031681565b6103c96103ac366004613cef565b600860209081526000928352604080842090915290825290205481565b604051908152602001610220565b6103ea6103e5366004613c94565b611b6a565b6040516102209190614086565b61023c610405366004613cb7565b611c74565b6001546102b0906001600160a01b031681565b6003546102519060ff1681565b61023c610438366004613ea5565b611d53565b61023c61044b366004613e73565b611ed5565b61023c61228c565b6103c9610466366004613c94565b600c6020526000908152604090205481565b61048b610486366004613cb7565b612389565b604080519215158352602083019190915201610220565b6000546102b0906201000090046001600160a01b031681565b61023c6104c9366004613d5b565b6123ec565b6103c960025481565b6102136104e5366004613ee9565b61258f565b600d546102b0906001600160a01b031681565b61023c61050b366004613c94565b61293f565b61052361051e366004613c94565b612a58565b6040516102209e9d9c9b9a9998979695949392919061417b565b61023c61054b366004613d1a565b61322e565b61023c61055e366004613cb7565b6132ae565b6102b0610571366004613dad565b60076020908152600093845260408085208252928452828420905282529020546001600160a01b031681565b6103ea6105ab366004613cef565b61331f565b6105c36105be366004613c94565b613334565b60405161022093929190614099565b6105da61334f565b6040516102209190600060c082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b61023c610634366004613c94565b6136a0565b604080516064808252610ca08201909252606091600091829160208201610c80803683370190505090506123295b61270f81101561087657600d54604051630923e33560e21b8152600481018390526000602482018190526127126044830152916001600160a01b03169063248f8cd49060640160206040518083038186803b1580156106c557600080fd5b505afa1580156106d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fd9190613fcb565b11806107925750600d54604051630923e33560e21b8152600481018390526000602482018190526127116044830152916001600160a01b03169063248f8cd49060640160206040518083038186803b15801561075857600080fd5b505afa15801561076c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107909190613fcb565b115b806108275750600d54604051630923e33560e21b815260048101839052612711602482015261271260448201526000916001600160a01b03169063248f8cd49060640160206040518083038186803b1580156107ed57600080fd5b505afa158015610801573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108259190613fcb565b115b15610864578082848151811061084d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101526108618361427b565b92505b8061086e8161427b565b915050610667565b508115610c4257816001600160401b038111156108a357634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156108ff57816020015b6108ec6040518060800160405280600081526020016000815260200160008152602001600081525090565b8152602001906001900390816108c15790505b50925060005b82811015610c405781818151811061092d57634e487b7160e01b600052603260045260246000fd5b602002602001015184828151811061095557634e487b7160e01b600052603260045260246000fd5b602090810291909101015152600d5482516001600160a01b039091169063248f8cd49084908490811061099857634e487b7160e01b600052603260045260246000fd5b60209081029190910101516040516001600160e01b031960e084901b168152600481019190915260006024820152612712604482015260640160206040518083038186803b1580156109e957600080fd5b505afa1580156109fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a219190613fcb565b848281518110610a4157634e487b7160e01b600052603260045260246000fd5b6020908102919091018101510152600d5482516001600160a01b039091169063248f8cd490849084908110610a8657634e487b7160e01b600052603260045260246000fd5b60209081029190910101516040516001600160e01b031960e084901b168152600481019190915260006024820152612711604482015260640160206040518083038186803b158015610ad757600080fd5b505afa158015610aeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0f9190613fcb565b848281518110610b2f57634e487b7160e01b600052603260045260246000fd5b602090810291909101015160400152600d5482516001600160a01b039091169063248f8cd490849084908110610b7557634e487b7160e01b600052603260045260246000fd5b60209081029190910101516040516001600160e01b031960e084901b16815260048101919091526127116024820152612712604482015260640160206040518083038186803b158015610bc757600080fd5b505afa158015610bdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bff9190613fcb565b848281518110610c1f57634e487b7160e01b600052603260045260246000fd5b60209081029190910101516060015280610c388161427b565b915050610905565b505b505090565b6001600160a01b038116610ca25760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f7420626520300000000000000060448201526064015b60405180910390fd5b600154600160a01b900460ff1615610d0e5760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610c99565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b03831662010000810262010000600160b01b03199092169190911782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91015b60405180910390a150565b6001600160a01b03831660009081526006602090815260408083208584529091528120610db490836136f6565b949350505050565b610dc4613777565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290602001610d7c565b610e1a613777565b60035460ff1615158115151415610e2e5750565b6003805460ff191682151590811790915560ff1615610e4c57426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec590602001610d7c565b50565b600080600080600080869050806001600160a01b031663158ef93e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610ecd57600080fd5b505afa158015610ee1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f059190613f74565b1561114a57806001600160a01b031663158ef93e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f4357600080fd5b505afa158015610f57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f7b9190613f74565b9550806001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b158015610fb657600080fd5b505afa158015610fca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fee9190613f74565b9450806001600160a01b0316635c975abb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561102957600080fd5b505afa15801561103d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110619190613f74565b9350806001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561109c57600080fd5b505afa1580156110b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d49190613f74565b9250806001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b15801561110f57600080fd5b505afa158015611123573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111479190613f74565b91505b5091939590929450565b606060008180808380808080805b8b518110156112b957600860008d838151811061118f57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006002815260200190815260200160002054600860008e84815181106111ee57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006001815260200190815260200160002054600860008f858151811061124d57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600080815260200190815260200160002054611291919061424c565b61129b919061424c565b6112a5908661424c565b9450806112b18161427b565b915050611162565b50836001600160401b038111156112e057634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611309578160200160208202803683370190505b50995060005b8b51811015611b5c576113488c828151811061133b57634e487b7160e01b600052603260045260246000fd5b60200260200101516137f1565b81518351939c50919a50985010611360578751611363565b88515b9550600087511180156113765750858751115b6113805785611383565b86515b95508b81815181106113a557634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b1580156113e557600080fd5b505afa1580156113f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061141d9190613f74565b80156114be57508b818151811061144457634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316639a82a09a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561148457600080fd5b505afa158015611498573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114bc9190613f74565b155b15611b4a578b81815181106114e357634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663653721476040518163ffffffff1660e01b815260040160206040518083038186803b15801561152357600080fd5b505afa158015611537573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061155b9190613fac565b600381111561157a57634e487b7160e01b600052602160045260246000fd5b925060005b86811015611b4857808a51111561176c578981815181106115b057634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156115f057600080fd5b505afa158015611604573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116289190613f74565b925089818151811061164a57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b15801561168a57600080fd5b505afa15801561169e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c29190613f74565b9450821580156116d657508360ff16600114155b806116f657508280156116ec57508360ff166001145b80156116f6575084155b9450841561176c5789818151811061171e57634e487b7160e01b600052603260045260246000fd5b60200260200101518c8c8151811061174657634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101528a6117688161427b565b9b50505b80895111156119515788818151811061179557634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156117d557600080fd5b505afa1580156117e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180d9190613f74565b925088818151811061182f57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b15801561186f57600080fd5b505afa158015611883573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118a79190613f74565b9450821580156118bb57508360ff16600214155b806118db57508280156118d157508360ff166002145b80156118db575084155b945084156119515788818151811061190357634e487b7160e01b600052603260045260246000fd5b60200260200101518c8c8151811061192b57634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101528a61194d8161427b565b9b50505b8088511115611b365787818151811061197a57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156119ba57600080fd5b505afa1580156119ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119f29190613f74565b9250878181518110611a1457634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b158015611a5457600080fd5b505afa158015611a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8c9190613f74565b945082158015611aa057508360ff16600314155b80611ac05750828015611ab657508360ff166003145b8015611ac0575084155b94508415611b3657878181518110611ae857634e487b7160e01b600052603260045260246000fd5b60200260200101518c8c81518110611b1057634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101528a611b328161427b565b9b50505b80611b408161427b565b91505061157f565b505b80611b548161427b565b91505061130f565b505050505050505050915091565b6001600160a01b0381166000908152600c60205260409020546060906001600160401b03811115611bab57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611bd4578160200160208202803683370190505b50905060005b6001600160a01b0383166000908152600c6020526040902054811015611c6e576001600160a01b038084166000908152600a602090815260408083208584529091529020548351911690839083908110611c4457634e487b7160e01b600052603260045260246000fd5b6001600160a01b039092166020928302919091019091015280611c668161427b565b915050611bda565b50919050565b600054610100900460ff16611c8f5760005460ff1615611c93565b303b155b611cf65760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610c99565b600054610100900460ff16158015611d18576000805461ffff19166101011790555b611d2183610c47565b600d80546001600160a01b0319166001600160a01b0384161790558015611d4e576000805461ff00191690555b505050565b60005b8251811015611d4e57600d5483516001600160a01b03909116906362ff08f690859084908110611d9657634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401611dc991906001600160a01b0391909116815260200190565b60206040518083038186803b158015611de157600080fd5b505afa158015611df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e199190613f74565b15611ec357600d5483516001600160a01b039091169063b53f731990859084908110611e5557634e487b7160e01b600052603260045260246000fd5b6020026020010151846040518363ffffffff1660e01b8152600401611e909291906001600160a01b0392831681529116602082015260400190565b600060405180830381600087803b158015611eaa57600080fd5b505af1158015611ebe573d6000803e3d6000fd5b505050505b80611ecd8161427b565b915050611d56565b600d5460408051639324cac760e01b815290516000926001600160a01b031691639324cac7916004808301926020929190829003018186803b158015611f1a57600080fd5b505afa158015611f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f529190613f90565b600d546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a082319060240160206040518083038186803b158015611f9657600080fd5b505afa158015611faa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fce9190613fcb565b905060005b825181101561214a57600d5483516001600160a01b03909116906362ff08f69085908490811061201357634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161204691906001600160a01b0391909116815260200190565b60206040518083038186803b15801561205e57600080fd5b505afa158015612072573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120969190613f74565b1561213857600d5483516001600160a01b039091169063ad385b91908590849081106120d257634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161210591906001600160a01b0391909116815260200190565b600060405180830381600087803b15801561211f57600080fd5b505af1158015612133573d6000803e3d6000fd5b505050505b806121428161427b565b915050611fd3565b50600d5460408051639324cac760e01b8152905183926001600160a01b031691639324cac7916004808301926020929190829003018186803b15801561218f57600080fd5b505afa1580156121a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c79190613f90565b600d546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a082319060240160206040518083038186803b15801561220b57600080fd5b505afa15801561221f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122439190613fcb565b61224d9190614264565b90507f7d86d4bcb1b9bd54dbb9c956ea742ee1f71c89d834a77c8bda357a27a63d9b398183604051612280929190614162565b60405180910390a15050565b6001546001600160a01b031633146123045760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610c99565b60005460015460408051620100009093046001600160a01b03908116845290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a1600180546000805462010000600160b01b0319166001600160a01b03831662010000021790556001600160a01b0319169055565b60008060005b60038110156123e4576001600160a01b038516600090815260066020908152604080832084845290915290206123c590856136f6565b156123d257600192508091505b806123dc8161427b565b91505061238f565b509250929050565b6123f461389e565b600d546001600160a01b0316331461243f5760405162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b21039b2b73232b960911b6044820152606401610c99565b6001600160a01b038281166000908152600b6020526040902054166124e8576001600160a01b038281166000908152600b6020908152604080832080546001600160a01b0319169486169485179055928252600c905220546124a290600161424c565b6001600160a01b038281166000908152600c60209081526040808320859055600a82528083209483529390529190912080546001600160a01b0319169184169190911790555b6001600160a01b0384166000908152600660209081526040808320868452909152902061251590836138e8565b6001600160a01b0384811660008181526007602090815260408083208884528252808320938352600882528083208884528083528184208054855294835290832080546001600160a01b031916958816959095179094558682529290925280546001929061258490849061424c565b909155505050505050565b6060816001600160401b038111156125b757634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561261357816020015b6126006040518060800160405280600081526020016000815260200160008152602001600081525090565b8152602001906001900390816125d55790505b50905060005b828110156129385783838281811061264157634e487b7160e01b600052603260045260246000fd5b9050602002013582828151811061266857634e487b7160e01b600052603260045260246000fd5b602090810291909101015152600d546001600160a01b031663248f8cd48585848181106126a557634e487b7160e01b600052603260045260246000fd5b6040516001600160e01b031960e086901b1681526020909102929092013560048301525060006024820152612712604482015260640160206040518083038186803b1580156126f357600080fd5b505afa158015612707573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061272b9190613fcb565b82828151811061274b57634e487b7160e01b600052603260045260246000fd5b6020908102919091018101510152600d546001600160a01b031663248f8cd485858481811061278a57634e487b7160e01b600052603260045260246000fd5b6040516001600160e01b031960e086901b1681526020909102929092013560048301525060006024820152612711604482015260640160206040518083038186803b1580156127d857600080fd5b505afa1580156127ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128109190613fcb565b82828151811061283057634e487b7160e01b600052603260045260246000fd5b602090810291909101015160400152600d546001600160a01b031663248f8cd485858481811061287057634e487b7160e01b600052603260045260246000fd5b6040516001600160e01b031960e086901b168152602090910292909201356004830152506127116024820152612712604482015260640160206040518083038186803b1580156128bf57600080fd5b505afa1580156128d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f79190613fcb565b82828151811061291757634e487b7160e01b600052603260045260246000fd5b602090810291909101015160600152806129308161427b565b915050612619565b5092915050565b612947613777565b6001600160a01b03811661298f5760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610c99565b600154600160a81b900460ff16156129df5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610c99565b600080546001600160a01b038381166201000081810262010000600160b01b031990941693909317938490556001805460ff60a81b1916600160a81b1790556040805193909404909116825260208201527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9101610d7c565b60008060008060008060008060608060608060608060008f9050806001600160a01b031663158ef93e6040518163ffffffff1660e01b815260040160206040518083038186803b158015612aab57600080fd5b505afa158015612abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae39190613f74565b1561321c57806001600160a01b031663081ad4946040518163ffffffff1660e01b815260040160206040518083038186803b158015612b2157600080fd5b505afa158015612b35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b599190613fcb565b9e50806001600160a01b031663aa8c217c6040518163ffffffff1660e01b815260040160206040518083038186803b158015612b9457600080fd5b505afa158015612ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bcc9190613fcb565b9d50806001600160a01b031663c9bc23fa6040518163ffffffff1660e01b815260040160206040518083038186803b158015612c0757600080fd5b505afa158015612c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3f9190613fcb565b9c50806001600160a01b0316632ccfdac06040518163ffffffff1660e01b815260040160206040518083038186803b158015612c7a57600080fd5b505afa158015612c8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cb29190613fcb565b9b50806001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b158015612ced57600080fd5b505afa158015612d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d259190613f74565b9a50806001600160a01b0316635c975abb6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d6057600080fd5b505afa158015612d74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d989190613f74565b9950806001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b158015612dd357600080fd5b505afa158015612de7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e0b9190613f74565b9850806001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b158015612e4657600080fd5b505afa158015612e5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e7e9190613f74565b97508e6001600160401b03811115612ea657634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612ecf578160200160208202803683370190505b5096508e6001600160401b03811115612ef857634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612f21578160200160208202803683370190505b5095508e6001600160401b03811115612f4a57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612f73578160200160208202803683370190505b5094508e6001600160401b03811115612f9c57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612fc5578160200160208202803683370190505b5093508e6001600160401b03811115612fee57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613017578160200160208202803683370190505b5092508e6001600160401b0381111561304057634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613069578160200160208202803683370190505b50915060005b8f81101561321a576040516302042e1d60e31b8152600481018290526001600160a01b0383169063102170e8906024016101006040518083038186803b1580156130b857600080fd5b505afa1580156130cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130f09190613de1565b9050508d878151811061311357634e487b7160e01b600052603260045260246000fd5b602002602001018d888151811061313a57634e487b7160e01b600052603260045260246000fd5b602002602001018d898151811061316157634e487b7160e01b600052603260045260246000fd5b602002602001018d8a8151811061318857634e487b7160e01b600052603260045260246000fd5b602002602001018d8b815181106131af57634e487b7160e01b600052603260045260246000fd5b602002602001018d8c815181106131d657634e487b7160e01b600052603260045260246000fd5b961515602097880291909101909601959095529415159093529390925292909252919091526001600160a01b039091169052806132128161427b565b91505061306f565b505b5091939597999b9d5091939597999b9d565b61323661389e565b600d546001600160a01b031633146132815760405162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b21039b2b73232b960911b6044820152606401610c99565b6001600160a01b03831660009081526006602090815260408083208584529091529020611d4e908261393b565b6132b661389e565b6001600160a01b0381166000908152600c60205260409020546132da90600161424c565b6001600160a01b039182166000908152600c60209081526040808320849055600a82528083209383529290522080546001600160a01b03191692909116919091179055565b606061332b8383613abe565b90505b92915050565b6060806060613342846137f1565b9196909550909350915050565b6133886040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060c00160405280600d60009054906101000a90046001600160a01b03166001600160a01b031663b06ed8326040518163ffffffff1660e01b815260040160206040518083038186803b1580156133e157600080fd5b505afa1580156133f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134199190613fcb565b8152600d546040805163422f96bf60e01b815290516020938401936001600160a01b039093169263422f96bf9260048082019391829003018186803b15801561346157600080fd5b505afa158015613475573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134999190613fcb565b8152600d546040805163e88698bf60e01b815290516020938401936001600160a01b039093169263e88698bf9260048082019391829003018186803b1580156134e157600080fd5b505afa1580156134f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135199190613fcb565b8152600d546040805163939909b760e01b815290516020938401936001600160a01b039093169263939909b79260048082019391829003018186803b15801561356157600080fd5b505afa158015613575573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135999190613fcb565b8152600d5460408051631ad3f6cd60e31b815290516020938401936001600160a01b039093169263d69fb6689260048082019391829003018186803b1580156135e157600080fd5b505afa1580156135f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136199190613fcb565b8152600d5460408051637341319360e01b815290516020938401936001600160a01b039093169263734131939260048082019391829003018186803b15801561366157600080fd5b505afa158015613675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136999190613fcb565b9052919050565b6136a8613777565b600d80546001600160a01b0319166001600160a01b0383169081179091556040519081527f8ead60d861e7aa1c4dd085c83f22affb53c375d4de371a5c6d41ab9ab2f0a18c90602001610d7c565b81546000906137075750600061332e565b6001600160a01b038216600090815260018401602052604090205480151580610db45750826001600160a01b03168460000160008154811061375957634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b031614949350505050565b6000546201000090046001600160a01b031633146137ef5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610c99565b565b6060806060613801846000613abe565b925061380e846001613abe565b91506002846001600160a01b0316631a1dbabb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561384b57600080fd5b505afa15801561385f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138839190613fcb565b111561389757613894846002613abe565b90505b9193909250565b600d546001600160a01b031633146137ef5760405162461bcd60e51b815260206004820152600d60248201526c4e6f74205061726c6179414d4d60981b6044820152606401610c99565b6138f282826136f6565b6139375781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555b5050565b61394582826136f6565b6139875760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b6044820152606401610c99565b6001600160a01b03811660009081526001808401602052604082205484549092916139b191614264565b9050808214613a595760008460000182815481106139df57634e487b7160e01b600052603260045260246000fd5b60009182526020909120015485546001600160a01b0390911691508190869085908110613a1c57634e487b7160e01b600052603260045260246000fd5b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b8354849080613a7857634e487b7160e01b600052603160045260246000fd5b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b6001600160a01b03821660009081526008602090815260408083208484529091529020546060906001600160401b03811115613b0a57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613b33578160200160208202803683370190505b50905060005b6001600160a01b0384166000908152600860209081526040808320868452909152902054811015612938576001600160a01b03808516600090815260076020908152604080832087845282528083208584529091529020548351911690839083908110613bb657634e487b7160e01b600052603260045260246000fd5b6001600160a01b039092166020928302919091019091015280613bd88161427b565b915050613b39565b8035613beb816142c2565b919050565b600082601f830112613c00578081fd5b813560206001600160401b0380831115613c1c57613c1c6142ac565b8260051b604051601f19603f83011681018181108482111715613c4157613c416142ac565b60405284815283810192508684018288018501891015613c5f578687fd5b8692505b85831015613c8857613c7481613be0565b845292840192600192909201918401613c63565b50979650505050505050565b600060208284031215613ca5578081fd5b8135613cb0816142c2565b9392505050565b60008060408385031215613cc9578081fd5b8235613cd4816142c2565b91506020830135613ce4816142c2565b809150509250929050565b60008060408385031215613d01578182fd5b8235613d0c816142c2565b946020939093013593505050565b600080600060608486031215613d2e578081fd5b8335613d39816142c2565b9250602084013591506040840135613d50816142c2565b809150509250925092565b60008060008060808587031215613d70578081fd5b8435613d7b816142c2565b9350602085013592506040850135613d92816142c2565b91506060850135613da2816142c2565b939692955090935050565b600080600060608486031215613dc1578283fd5b8335613dcc816142c2565b95602085013595506040909401359392505050565b600080600080600080600080610100898b031215613dfd578384fd5b8851613e08816142c2565b809850506020890151965060408901519550606089015194506080890151613e2f816142d7565b60a08a0151909450613e40816142d7565b60c08a0151909350613e51816142d7565b60e08a0151909250613e62816142d7565b809150509295985092959890939650565b600060208284031215613e84578081fd5b81356001600160401b03811115613e99578182fd5b610db484828501613bf0565b60008060408385031215613eb7578182fd5b82356001600160401b03811115613ecc578283fd5b613ed885828601613bf0565b9250506020830135613ce4816142c2565b60008060208385031215613efb578182fd5b82356001600160401b0380821115613f11578384fd5b818501915085601f830112613f24578384fd5b813581811115613f32578485fd5b8660208260051b8501011115613f46578485fd5b60209290920196919550909350505050565b600060208284031215613f69578081fd5b8135613cb0816142d7565b600060208284031215613f85578081fd5b8151613cb0816142d7565b600060208284031215613fa1578081fd5b8151613cb0816142c2565b600060208284031215613fbd578081fd5b815160048110613cb0578182fd5b600060208284031215613fdc578081fd5b5051919050565b6000815180845260208085019450808401835b8381101561401b5781516001600160a01b031687529582019590820190600101613ff6565b509495945050505050565b6000815180845260208085019450808401835b8381101561401b578151151587529582019590820190600101614039565b6000815180845260208085019450808401835b8381101561401b5781518752958201959082019060010161406a565b60208152600061332b6020830184613fe3565b6060815260006140ac6060830186613fe3565b82810360208401526140be8186613fe3565b905082810360408401526140d28185613fe3565b9695505050505050565b6040815260006140ef6040830185613fe3565b90508260208301529392505050565b602080825282518282018190526000919060409081850190868401855b828110156141555781518051855286810151878601528581015186860152606090810151908501526080909301929085019060010161411b565b5091979650505050505050565b828152604060208201526000610db46040830184613fe3565b8e81528d60208201528c60408201528b606082015261419e608082018c15159052565b89151560a082015288151560c082015287151560e08201526101c061010082015260006141cf6101c0830189613fe3565b8281036101208401526141e28189614057565b90508281036101408401526141f78188614057565b905082810361016084015261420c8187614057565b90508281036101808401526142218186614026565b90508281036101a08401526142368185614026565b9150509f9e505050505050505050505050505050565b6000821982111561425f5761425f614296565b500190565b60008282101561427657614276614296565b500390565b600060001982141561428f5761428f614296565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610e8557600080fd5b8015158114610e8557600080fdfea26469706673582212206aaaa58ac655d53ca3aa6fe3eb720d7286d9a06ccb4678723213ae80b0c1260064736f6c63430008040033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102065760003560e01c806379ba50971161011a578063c3b83f5f116100ad578063e1d4746a1161007c578063e1d4746a14610563578063e58100e41461059d578063e5bf48fc146105b0578063f5687198146105d2578063febec2a71461062657600080fd5b8063c3b83f5f146104fd578063c9eb8c9214610510578063cc39a1d71461053d578063cd4203c31461055057600080fd5b80638e1dadb4116100e95780638e1dadb4146104bb57806391b4ded9146104ce578063a2c94618146104d7578063be583ec7146104ea57600080fd5b806379ba50971461045057806384084933146104585780638d4ea476146104785780638da5cb5b146104a257600080fd5b80632f47a0f31161019d578063485cc9551161016c578063485cc955146103f757806353a47bb71461040a5780635c975abb1461041d5780636853366d1461042a5780636d9d3d461461043d57600080fd5b80632f47a0f314610349578063311a3b731461036a57806337a897d01461039e578063467979aa146103d757600080fd5b806316c38b3c116101d957806316c38b3c146102745780631955003d146102875780631a3dcf2a146102c857806323a831291461030d57600080fd5b80630b9ffc4e1461020b57806313af403514610229578063159fe4211461023e5780631627540c14610261575b600080fd5b610213610639565b60405161022091906140fe565b60405180910390f35b61023c610237366004613c94565b610c47565b005b61025161024c366004613d1a565b610d87565b6040519015158152602001610220565b61023c61026f366004613c94565b610dbc565b61023c610282366004613f58565b610e12565b6102b0610295366004613c94565b600b602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610220565b6102db6102d6366004613c94565b610e88565b60408051951515865293151560208601529115159284019290925290151560608301521515608082015260a001610220565b61033461031b366004613c94565b6009602052600090815260409020805460019091015482565b60408051928352602083019190915201610220565b61035c610357366004613e73565b611154565b6040516102209291906140dc565b6102b0610378366004613cef565b600a6020908152600092835260408084209091529082529020546001600160a01b031681565b6103c96103ac366004613cef565b600860209081526000928352604080842090915290825290205481565b604051908152602001610220565b6103ea6103e5366004613c94565b611b6a565b6040516102209190614086565b61023c610405366004613cb7565b611c74565b6001546102b0906001600160a01b031681565b6003546102519060ff1681565b61023c610438366004613ea5565b611d53565b61023c61044b366004613e73565b611ed5565b61023c61228c565b6103c9610466366004613c94565b600c6020526000908152604090205481565b61048b610486366004613cb7565b612389565b604080519215158352602083019190915201610220565b6000546102b0906201000090046001600160a01b031681565b61023c6104c9366004613d5b565b6123ec565b6103c960025481565b6102136104e5366004613ee9565b61258f565b600d546102b0906001600160a01b031681565b61023c61050b366004613c94565b61293f565b61052361051e366004613c94565b612a58565b6040516102209e9d9c9b9a9998979695949392919061417b565b61023c61054b366004613d1a565b61322e565b61023c61055e366004613cb7565b6132ae565b6102b0610571366004613dad565b60076020908152600093845260408085208252928452828420905282529020546001600160a01b031681565b6103ea6105ab366004613cef565b61331f565b6105c36105be366004613c94565b613334565b60405161022093929190614099565b6105da61334f565b6040516102209190600060c082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b61023c610634366004613c94565b6136a0565b604080516064808252610ca08201909252606091600091829160208201610c80803683370190505090506123295b61270f81101561087657600d54604051630923e33560e21b8152600481018390526000602482018190526127126044830152916001600160a01b03169063248f8cd49060640160206040518083038186803b1580156106c557600080fd5b505afa1580156106d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fd9190613fcb565b11806107925750600d54604051630923e33560e21b8152600481018390526000602482018190526127116044830152916001600160a01b03169063248f8cd49060640160206040518083038186803b15801561075857600080fd5b505afa15801561076c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107909190613fcb565b115b806108275750600d54604051630923e33560e21b815260048101839052612711602482015261271260448201526000916001600160a01b03169063248f8cd49060640160206040518083038186803b1580156107ed57600080fd5b505afa158015610801573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108259190613fcb565b115b15610864578082848151811061084d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101526108618361427b565b92505b8061086e8161427b565b915050610667565b508115610c4257816001600160401b038111156108a357634e487b7160e01b600052604160045260246000fd5b6040519080825280602002602001820160405280156108ff57816020015b6108ec6040518060800160405280600081526020016000815260200160008152602001600081525090565b8152602001906001900390816108c15790505b50925060005b82811015610c405781818151811061092d57634e487b7160e01b600052603260045260246000fd5b602002602001015184828151811061095557634e487b7160e01b600052603260045260246000fd5b602090810291909101015152600d5482516001600160a01b039091169063248f8cd49084908490811061099857634e487b7160e01b600052603260045260246000fd5b60209081029190910101516040516001600160e01b031960e084901b168152600481019190915260006024820152612712604482015260640160206040518083038186803b1580156109e957600080fd5b505afa1580156109fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a219190613fcb565b848281518110610a4157634e487b7160e01b600052603260045260246000fd5b6020908102919091018101510152600d5482516001600160a01b039091169063248f8cd490849084908110610a8657634e487b7160e01b600052603260045260246000fd5b60209081029190910101516040516001600160e01b031960e084901b168152600481019190915260006024820152612711604482015260640160206040518083038186803b158015610ad757600080fd5b505afa158015610aeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0f9190613fcb565b848281518110610b2f57634e487b7160e01b600052603260045260246000fd5b602090810291909101015160400152600d5482516001600160a01b039091169063248f8cd490849084908110610b7557634e487b7160e01b600052603260045260246000fd5b60209081029190910101516040516001600160e01b031960e084901b16815260048101919091526127116024820152612712604482015260640160206040518083038186803b158015610bc757600080fd5b505afa158015610bdb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bff9190613fcb565b848281518110610c1f57634e487b7160e01b600052603260045260246000fd5b60209081029190910101516060015280610c388161427b565b915050610905565b505b505090565b6001600160a01b038116610ca25760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f7420626520300000000000000060448201526064015b60405180910390fd5b600154600160a01b900460ff1615610d0e5760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610c99565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b03831662010000810262010000600160b01b03199092169190911782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91015b60405180910390a150565b6001600160a01b03831660009081526006602090815260408083208584529091528120610db490836136f6565b949350505050565b610dc4613777565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290602001610d7c565b610e1a613777565b60035460ff1615158115151415610e2e5750565b6003805460ff191682151590811790915560ff1615610e4c57426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec590602001610d7c565b50565b600080600080600080869050806001600160a01b031663158ef93e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610ecd57600080fd5b505afa158015610ee1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f059190613f74565b1561114a57806001600160a01b031663158ef93e6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f4357600080fd5b505afa158015610f57573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f7b9190613f74565b9550806001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b158015610fb657600080fd5b505afa158015610fca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fee9190613f74565b9450806001600160a01b0316635c975abb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561102957600080fd5b505afa15801561103d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110619190613f74565b9350806001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b15801561109c57600080fd5b505afa1580156110b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110d49190613f74565b9250806001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b15801561110f57600080fd5b505afa158015611123573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111479190613f74565b91505b5091939590929450565b606060008180808380808080805b8b518110156112b957600860008d838151811061118f57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006002815260200190815260200160002054600860008e84815181106111ee57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006001815260200190815260200160002054600860008f858151811061124d57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020600080815260200190815260200160002054611291919061424c565b61129b919061424c565b6112a5908661424c565b9450806112b18161427b565b915050611162565b50836001600160401b038111156112e057634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611309578160200160208202803683370190505b50995060005b8b51811015611b5c576113488c828151811061133b57634e487b7160e01b600052603260045260246000fd5b60200260200101516137f1565b81518351939c50919a50985010611360578751611363565b88515b9550600087511180156113765750858751115b6113805785611383565b86515b95508b81815181106113a557634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b1580156113e557600080fd5b505afa1580156113f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061141d9190613f74565b80156114be57508b818151811061144457634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316639a82a09a6040518163ffffffff1660e01b815260040160206040518083038186803b15801561148457600080fd5b505afa158015611498573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114bc9190613f74565b155b15611b4a578b81815181106114e357634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663653721476040518163ffffffff1660e01b815260040160206040518083038186803b15801561152357600080fd5b505afa158015611537573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061155b9190613fac565b600381111561157a57634e487b7160e01b600052602160045260246000fd5b925060005b86811015611b4857808a51111561176c578981815181106115b057634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156115f057600080fd5b505afa158015611604573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116289190613f74565b925089818151811061164a57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b15801561168a57600080fd5b505afa15801561169e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c29190613f74565b9450821580156116d657508360ff16600114155b806116f657508280156116ec57508360ff166001145b80156116f6575084155b9450841561176c5789818151811061171e57634e487b7160e01b600052603260045260246000fd5b60200260200101518c8c8151811061174657634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101528a6117688161427b565b9b50505b80895111156119515788818151811061179557634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156117d557600080fd5b505afa1580156117e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061180d9190613f74565b925088818151811061182f57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b15801561186f57600080fd5b505afa158015611883573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118a79190613f74565b9450821580156118bb57508360ff16600214155b806118db57508280156118d157508360ff166002145b80156118db575084155b945084156119515788818151811061190357634e487b7160e01b600052603260045260246000fd5b60200260200101518c8c8151811061192b57634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101528a61194d8161427b565b9b50505b8088511115611b365787818151811061197a57634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b1580156119ba57600080fd5b505afa1580156119ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119f29190613f74565b9250878181518110611a1457634e487b7160e01b600052603260045260246000fd5b60200260200101516001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b158015611a5457600080fd5b505afa158015611a68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8c9190613f74565b945082158015611aa057508360ff16600314155b80611ac05750828015611ab657508360ff166003145b8015611ac0575084155b94508415611b3657878181518110611ae857634e487b7160e01b600052603260045260246000fd5b60200260200101518c8c81518110611b1057634e487b7160e01b600052603260045260246000fd5b6001600160a01b03909216602092830291909101909101528a611b328161427b565b9b50505b80611b408161427b565b91505061157f565b505b80611b548161427b565b91505061130f565b505050505050505050915091565b6001600160a01b0381166000908152600c60205260409020546060906001600160401b03811115611bab57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015611bd4578160200160208202803683370190505b50905060005b6001600160a01b0383166000908152600c6020526040902054811015611c6e576001600160a01b038084166000908152600a602090815260408083208584529091529020548351911690839083908110611c4457634e487b7160e01b600052603260045260246000fd5b6001600160a01b039092166020928302919091019091015280611c668161427b565b915050611bda565b50919050565b600054610100900460ff16611c8f5760005460ff1615611c93565b303b155b611cf65760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610c99565b600054610100900460ff16158015611d18576000805461ffff19166101011790555b611d2183610c47565b600d80546001600160a01b0319166001600160a01b0384161790558015611d4e576000805461ff00191690555b505050565b60005b8251811015611d4e57600d5483516001600160a01b03909116906362ff08f690859084908110611d9657634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b8152600401611dc991906001600160a01b0391909116815260200190565b60206040518083038186803b158015611de157600080fd5b505afa158015611df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e199190613f74565b15611ec357600d5483516001600160a01b039091169063b53f731990859084908110611e5557634e487b7160e01b600052603260045260246000fd5b6020026020010151846040518363ffffffff1660e01b8152600401611e909291906001600160a01b0392831681529116602082015260400190565b600060405180830381600087803b158015611eaa57600080fd5b505af1158015611ebe573d6000803e3d6000fd5b505050505b80611ecd8161427b565b915050611d56565b600d5460408051639324cac760e01b815290516000926001600160a01b031691639324cac7916004808301926020929190829003018186803b158015611f1a57600080fd5b505afa158015611f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f529190613f90565b600d546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a082319060240160206040518083038186803b158015611f9657600080fd5b505afa158015611faa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fce9190613fcb565b905060005b825181101561214a57600d5483516001600160a01b03909116906362ff08f69085908490811061201357634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161204691906001600160a01b0391909116815260200190565b60206040518083038186803b15801561205e57600080fd5b505afa158015612072573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120969190613f74565b1561213857600d5483516001600160a01b039091169063ad385b91908590849081106120d257634e487b7160e01b600052603260045260246000fd5b60200260200101516040518263ffffffff1660e01b815260040161210591906001600160a01b0391909116815260200190565b600060405180830381600087803b15801561211f57600080fd5b505af1158015612133573d6000803e3d6000fd5b505050505b806121428161427b565b915050611fd3565b50600d5460408051639324cac760e01b8152905183926001600160a01b031691639324cac7916004808301926020929190829003018186803b15801561218f57600080fd5b505afa1580156121a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c79190613f90565b600d546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a082319060240160206040518083038186803b15801561220b57600080fd5b505afa15801561221f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122439190613fcb565b61224d9190614264565b90507f7d86d4bcb1b9bd54dbb9c956ea742ee1f71c89d834a77c8bda357a27a63d9b398183604051612280929190614162565b60405180910390a15050565b6001546001600160a01b031633146123045760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610c99565b60005460015460408051620100009093046001600160a01b03908116845290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a1600180546000805462010000600160b01b0319166001600160a01b03831662010000021790556001600160a01b0319169055565b60008060005b60038110156123e4576001600160a01b038516600090815260066020908152604080832084845290915290206123c590856136f6565b156123d257600192508091505b806123dc8161427b565b91505061238f565b509250929050565b6123f461389e565b600d546001600160a01b0316331461243f5760405162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b21039b2b73232b960911b6044820152606401610c99565b6001600160a01b038281166000908152600b6020526040902054166124e8576001600160a01b038281166000908152600b6020908152604080832080546001600160a01b0319169486169485179055928252600c905220546124a290600161424c565b6001600160a01b038281166000908152600c60209081526040808320859055600a82528083209483529390529190912080546001600160a01b0319169184169190911790555b6001600160a01b0384166000908152600660209081526040808320868452909152902061251590836138e8565b6001600160a01b0384811660008181526007602090815260408083208884528252808320938352600882528083208884528083528184208054855294835290832080546001600160a01b031916958816959095179094558682529290925280546001929061258490849061424c565b909155505050505050565b6060816001600160401b038111156125b757634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561261357816020015b6126006040518060800160405280600081526020016000815260200160008152602001600081525090565b8152602001906001900390816125d55790505b50905060005b828110156129385783838281811061264157634e487b7160e01b600052603260045260246000fd5b9050602002013582828151811061266857634e487b7160e01b600052603260045260246000fd5b602090810291909101015152600d546001600160a01b031663248f8cd48585848181106126a557634e487b7160e01b600052603260045260246000fd5b6040516001600160e01b031960e086901b1681526020909102929092013560048301525060006024820152612712604482015260640160206040518083038186803b1580156126f357600080fd5b505afa158015612707573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061272b9190613fcb565b82828151811061274b57634e487b7160e01b600052603260045260246000fd5b6020908102919091018101510152600d546001600160a01b031663248f8cd485858481811061278a57634e487b7160e01b600052603260045260246000fd5b6040516001600160e01b031960e086901b1681526020909102929092013560048301525060006024820152612711604482015260640160206040518083038186803b1580156127d857600080fd5b505afa1580156127ec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128109190613fcb565b82828151811061283057634e487b7160e01b600052603260045260246000fd5b602090810291909101015160400152600d546001600160a01b031663248f8cd485858481811061287057634e487b7160e01b600052603260045260246000fd5b6040516001600160e01b031960e086901b168152602090910292909201356004830152506127116024820152612712604482015260640160206040518083038186803b1580156128bf57600080fd5b505afa1580156128d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128f79190613fcb565b82828151811061291757634e487b7160e01b600052603260045260246000fd5b602090810291909101015160600152806129308161427b565b915050612619565b5092915050565b612947613777565b6001600160a01b03811661298f5760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610c99565b600154600160a81b900460ff16156129df5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610c99565b600080546001600160a01b038381166201000081810262010000600160b01b031990941693909317938490556001805460ff60a81b1916600160a81b1790556040805193909404909116825260208201527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c9101610d7c565b60008060008060008060008060608060608060608060008f9050806001600160a01b031663158ef93e6040518163ffffffff1660e01b815260040160206040518083038186803b158015612aab57600080fd5b505afa158015612abf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae39190613f74565b1561321c57806001600160a01b031663081ad4946040518163ffffffff1660e01b815260040160206040518083038186803b158015612b2157600080fd5b505afa158015612b35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b599190613fcb565b9e50806001600160a01b031663aa8c217c6040518163ffffffff1660e01b815260040160206040518083038186803b158015612b9457600080fd5b505afa158015612ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bcc9190613fcb565b9d50806001600160a01b031663c9bc23fa6040518163ffffffff1660e01b815260040160206040518083038186803b158015612c0757600080fd5b505afa158015612c1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c3f9190613fcb565b9c50806001600160a01b0316632ccfdac06040518163ffffffff1660e01b815260040160206040518083038186803b158015612c7a57600080fd5b505afa158015612c8e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cb29190613fcb565b9b50806001600160a01b0316633f6fa6556040518163ffffffff1660e01b815260040160206040518083038186803b158015612ced57600080fd5b505afa158015612d01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d259190613f74565b9a50806001600160a01b0316635c975abb6040518163ffffffff1660e01b815260040160206040518083038186803b158015612d6057600080fd5b505afa158015612d74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d989190613f74565b9950806001600160a01b0316635dd2317e6040518163ffffffff1660e01b815260040160206040518083038186803b158015612dd357600080fd5b505afa158015612de7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e0b9190613f74565b9850806001600160a01b031663af4c55796040518163ffffffff1660e01b815260040160206040518083038186803b158015612e4657600080fd5b505afa158015612e5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e7e9190613f74565b97508e6001600160401b03811115612ea657634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612ecf578160200160208202803683370190505b5096508e6001600160401b03811115612ef857634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612f21578160200160208202803683370190505b5095508e6001600160401b03811115612f4a57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612f73578160200160208202803683370190505b5094508e6001600160401b03811115612f9c57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015612fc5578160200160208202803683370190505b5093508e6001600160401b03811115612fee57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613017578160200160208202803683370190505b5092508e6001600160401b0381111561304057634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613069578160200160208202803683370190505b50915060005b8f81101561321a576040516302042e1d60e31b8152600481018290526001600160a01b0383169063102170e8906024016101006040518083038186803b1580156130b857600080fd5b505afa1580156130cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130f09190613de1565b9050508d878151811061311357634e487b7160e01b600052603260045260246000fd5b602002602001018d888151811061313a57634e487b7160e01b600052603260045260246000fd5b602002602001018d898151811061316157634e487b7160e01b600052603260045260246000fd5b602002602001018d8a8151811061318857634e487b7160e01b600052603260045260246000fd5b602002602001018d8b815181106131af57634e487b7160e01b600052603260045260246000fd5b602002602001018d8c815181106131d657634e487b7160e01b600052603260045260246000fd5b961515602097880291909101909601959095529415159093529390925292909252919091526001600160a01b039091169052806132128161427b565b91505061306f565b505b5091939597999b9d5091939597999b9d565b61323661389e565b600d546001600160a01b031633146132815760405162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b21039b2b73232b960911b6044820152606401610c99565b6001600160a01b03831660009081526006602090815260408083208584529091529020611d4e908261393b565b6132b661389e565b6001600160a01b0381166000908152600c60205260409020546132da90600161424c565b6001600160a01b039182166000908152600c60209081526040808320849055600a82528083209383529290522080546001600160a01b03191692909116919091179055565b606061332b8383613abe565b90505b92915050565b6060806060613342846137f1565b9196909550909350915050565b6133886040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040518060c00160405280600d60009054906101000a90046001600160a01b03166001600160a01b031663b06ed8326040518163ffffffff1660e01b815260040160206040518083038186803b1580156133e157600080fd5b505afa1580156133f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134199190613fcb565b8152600d546040805163422f96bf60e01b815290516020938401936001600160a01b039093169263422f96bf9260048082019391829003018186803b15801561346157600080fd5b505afa158015613475573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134999190613fcb565b8152600d546040805163e88698bf60e01b815290516020938401936001600160a01b039093169263e88698bf9260048082019391829003018186803b1580156134e157600080fd5b505afa1580156134f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135199190613fcb565b8152600d546040805163939909b760e01b815290516020938401936001600160a01b039093169263939909b79260048082019391829003018186803b15801561356157600080fd5b505afa158015613575573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135999190613fcb565b8152600d5460408051631ad3f6cd60e31b815290516020938401936001600160a01b039093169263d69fb6689260048082019391829003018186803b1580156135e157600080fd5b505afa1580156135f5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136199190613fcb565b8152600d5460408051637341319360e01b815290516020938401936001600160a01b039093169263734131939260048082019391829003018186803b15801561366157600080fd5b505afa158015613675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136999190613fcb565b9052919050565b6136a8613777565b600d80546001600160a01b0319166001600160a01b0383169081179091556040519081527f8ead60d861e7aa1c4dd085c83f22affb53c375d4de371a5c6d41ab9ab2f0a18c90602001610d7c565b81546000906137075750600061332e565b6001600160a01b038216600090815260018401602052604090205480151580610db45750826001600160a01b03168460000160008154811061375957634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b031614949350505050565b6000546201000090046001600160a01b031633146137ef5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610c99565b565b6060806060613801846000613abe565b925061380e846001613abe565b91506002846001600160a01b0316631a1dbabb6040518163ffffffff1660e01b815260040160206040518083038186803b15801561384b57600080fd5b505afa15801561385f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138839190613fcb565b111561389757613894846002613abe565b90505b9193909250565b600d546001600160a01b031633146137ef5760405162461bcd60e51b815260206004820152600d60248201526c4e6f74205061726c6179414d4d60981b6044820152606401610c99565b6138f282826136f6565b6139375781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555b5050565b61394582826136f6565b6139875760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b6044820152606401610c99565b6001600160a01b03811660009081526001808401602052604082205484549092916139b191614264565b9050808214613a595760008460000182815481106139df57634e487b7160e01b600052603260045260246000fd5b60009182526020909120015485546001600160a01b0390911691508190869085908110613a1c57634e487b7160e01b600052603260045260246000fd5b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b8354849080613a7857634e487b7160e01b600052603160045260246000fd5b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b6001600160a01b03821660009081526008602090815260408083208484529091529020546060906001600160401b03811115613b0a57634e487b7160e01b600052604160045260246000fd5b604051908082528060200260200182016040528015613b33578160200160208202803683370190505b50905060005b6001600160a01b0384166000908152600860209081526040808320868452909152902054811015612938576001600160a01b03808516600090815260076020908152604080832087845282528083208584529091529020548351911690839083908110613bb657634e487b7160e01b600052603260045260246000fd5b6001600160a01b039092166020928302919091019091015280613bd88161427b565b915050613b39565b8035613beb816142c2565b919050565b600082601f830112613c00578081fd5b813560206001600160401b0380831115613c1c57613c1c6142ac565b8260051b604051601f19603f83011681018181108482111715613c4157613c416142ac565b60405284815283810192508684018288018501891015613c5f578687fd5b8692505b85831015613c8857613c7481613be0565b845292840192600192909201918401613c63565b50979650505050505050565b600060208284031215613ca5578081fd5b8135613cb0816142c2565b9392505050565b60008060408385031215613cc9578081fd5b8235613cd4816142c2565b91506020830135613ce4816142c2565b809150509250929050565b60008060408385031215613d01578182fd5b8235613d0c816142c2565b946020939093013593505050565b600080600060608486031215613d2e578081fd5b8335613d39816142c2565b9250602084013591506040840135613d50816142c2565b809150509250925092565b60008060008060808587031215613d70578081fd5b8435613d7b816142c2565b9350602085013592506040850135613d92816142c2565b91506060850135613da2816142c2565b939692955090935050565b600080600060608486031215613dc1578283fd5b8335613dcc816142c2565b95602085013595506040909401359392505050565b600080600080600080600080610100898b031215613dfd578384fd5b8851613e08816142c2565b809850506020890151965060408901519550606089015194506080890151613e2f816142d7565b60a08a0151909450613e40816142d7565b60c08a0151909350613e51816142d7565b60e08a0151909250613e62816142d7565b809150509295985092959890939650565b600060208284031215613e84578081fd5b81356001600160401b03811115613e99578182fd5b610db484828501613bf0565b60008060408385031215613eb7578182fd5b82356001600160401b03811115613ecc578283fd5b613ed885828601613bf0565b9250506020830135613ce4816142c2565b60008060208385031215613efb578182fd5b82356001600160401b0380821115613f11578384fd5b818501915085601f830112613f24578384fd5b813581811115613f32578485fd5b8660208260051b8501011115613f46578485fd5b60209290920196919550909350505050565b600060208284031215613f69578081fd5b8135613cb0816142d7565b600060208284031215613f85578081fd5b8151613cb0816142d7565b600060208284031215613fa1578081fd5b8151613cb0816142c2565b600060208284031215613fbd578081fd5b815160048110613cb0578182fd5b600060208284031215613fdc578081fd5b5051919050565b6000815180845260208085019450808401835b8381101561401b5781516001600160a01b031687529582019590820190600101613ff6565b509495945050505050565b6000815180845260208085019450808401835b8381101561401b578151151587529582019590820190600101614039565b6000815180845260208085019450808401835b8381101561401b5781518752958201959082019060010161406a565b60208152600061332b6020830184613fe3565b6060815260006140ac6060830186613fe3565b82810360208401526140be8186613fe3565b905082810360408401526140d28185613fe3565b9695505050505050565b6040815260006140ef6040830185613fe3565b90508260208301529392505050565b602080825282518282018190526000919060409081850190868401855b828110156141555781518051855286810151878601528581015186860152606090810151908501526080909301929085019060010161411b565b5091979650505050505050565b828152604060208201526000610db46040830184613fe3565b8e81528d60208201528c60408201528b606082015261419e608082018c15159052565b89151560a082015288151560c082015287151560e08201526101c061010082015260006141cf6101c0830189613fe3565b8281036101208401526141e28189614057565b90508281036101408401526141f78188614057565b905082810361016084015261420c8187614057565b90508281036101808401526142218186614026565b90508281036101a08401526142368185614026565b9150509f9e505050505050505050505050505050565b6000821982111561425f5761425f614296565b500190565b60008282101561427657614276614296565b500390565b600060001982141561428f5761428f614296565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610e8557600080fd5b8015158114610e8557600080fdfea26469706673582212206aaaa58ac655d53ca3aa6fe3eb720d7286d9a06ccb4678723213ae80b0c1260064736f6c63430008040033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.