Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
SportsAMMV2
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 100 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
// internal
import "../../utils/proxy/ProxyReentrancyGuard.sol";
import "../../utils/proxy/ProxyOwned.sol";
import "../../utils/proxy/ProxyPausable.sol";
import "@thales-dao/contracts/contracts/interfaces/IReferrals.sol";
import "@thales-dao/contracts/contracts/interfaces/IMultiCollateralOnOffRamp.sol";
import "@thales-dao/contracts/contracts/interfaces/IStakingThales.sol";
import "@thales-dao/contracts/contracts/interfaces/IPriceFeed.sol";
import "./Ticket.sol";
import "../../interfaces/ISportsAMMV2.sol";
import "../../interfaces/ISportsAMMV2Manager.sol";
import "../../interfaces/ISportsAMMV2RiskManager.sol";
import "../../interfaces/ISportsAMMV2ResultManager.sol";
import "../../interfaces/ISportsAMMV2LiquidityPool.sol";
import "../../interfaces/IWeth.sol";
import "../../interfaces/IProxyBetting.sol";
/// @title Sports AMM V2 contract
/// @author vladan
contract SportsAMMV2 is Initializable, ProxyOwned, ProxyPausable, ProxyReentrancyGuard {
/* ========== LIBRARIES ========== */
using SafeERC20 for IERC20;
/* ========== CONST VARIABLES ========== */
uint private constant ONE = 1e18;
uint private constant MAX_APPROVAL = type(uint256).max;
uint8 private constant NON_SYSTEM_BET = 0;
/* ========== ERRORS ========== */
error SBDOutOfRange();
error IllegalInputAmounts();
error OnlyOwner();
error OnlyDedicatedProcessor();
error UndefinedRecipient();
error UnknownTicket();
error UnsupportedSender();
error OfframpOnlyDefaultCollateralAllowed();
error InsuffETHSent();
error ZeroPriceForCollateral();
error MultiCollateralDisabled();
error InsuffReceived();
error InvalidLength();
error InvalidSender();
error ZeroAmount();
error InvalidPosition();
error MultiCollatDisabled();
error OnlyTicketOwner();
/* ========== STRUCT VARIABLES ========== */
struct TradeDataInternal {
uint _buyInAmount;
uint _expectedPayout;
uint _additionalSlippage;
address _recipient;
bool _isLive;
address _collateral;
address _collateralPool;
uint _collateralPriceInUSD;
bool _isSGP;
}
/* ========== STATE VARIABLES ========== */
// merkle tree root per game
mapping(bytes32 => bytes32) public rootPerGame;
// the default token used for payment
IERC20 public defaultCollateral;
// Liquidity pool instance for the given collateral
mapping(address => address) public liquidityPoolForCollateral;
// decimals of the default collateral
uint private defaultCollateralDecimals;
// manager address
ISportsAMMV2Manager public manager;
// risk manager address
ISportsAMMV2RiskManager public riskManager;
// result manager address
ISportsAMMV2ResultManager public resultManager;
// referrals address
IReferrals public referrals;
// ticket mastercopy address
address public ticketMastercopy;
// safe box address
address public safeBox;
// safe box fee paid on each trade
uint public safeBoxFee;
// multi-collateral on/off ramp address
IMultiCollateralOnOffRamp public multiCollateralOnOffRamp;
// is multi-collateral enabled
bool private multicollateralEnabled;
// staking thales address
IStakingThales private stakingThales;
// CL client that processes live requests
address public liveTradingProcessor;
// the contract that processes all free bets
address public freeBetsHolder;
// support bonus payouts for some collaterals (e.g. THALES)
mapping(address => uint) public addedPayoutPercentagePerCollateral;
// support different SB per collateral, namely THALES as a collateral will be directly burned
mapping(address => address) public safeBoxPerCollateral;
// the contract that processes betting with StakedTHALES
address public stakingThalesBettingProxy;
struct TradeDataQuoteInternal {
uint _buyInAmount;
bool _shouldCheckRisks;
uint _buyInAmountInDefaultCollateral;
address _collateral;
bool _isLive;
bool _isSGP;
uint _approvedQuote;
}
struct TradeProcessingParams {
uint _totalQuote;
uint _payout;
uint _fees;
uint _addedPayoutPercentage;
uint _payoutWithFees;
}
// CL client that processes SGP requests
address public sgpTradingProcessor;
struct TradeTypeData {
uint8 _systemBetDenominator;
bool _isSGP;
bool _isLive;
}
// declare that it can receive eth
receive() external payable {}
/* ========== CONSTRUCTOR ========== */
/// @notice initialize the storage in the proxy contract with the parameters
/// @param _owner owner for using the onlyOwner functions
function initialize(address _owner) public initializer {
setOwner(_owner);
initNonReentrant();
}
/* ========== EXTERNAL READ FUNCTIONS ========== */
/// @notice get roots for the list of games
/// @param _games to return roots for
/// @notice get roots for the list of games
/// @param _games to return roots for
function getRootsPerGames(bytes32[] calldata _games) external view returns (bytes32[] memory _roots) {
uint len = _games.length;
_roots = new bytes32[](len);
unchecked {
for (uint i; i < len; ++i) {
_roots[i] = rootPerGame[_games[i]];
}
}
}
/// @notice gets trade quote
/// @param _tradeData trade data with all market info needed for ticket
/// @param _buyInAmount ticket buy-in amount
/// @param _collateral different collateral used for payment
/// @param _isLive whether this is a live bet
/// @return totalQuote total ticket quote
/// @return payout expected payout
/// @return fees ticket fees
/// @return amountsToBuy amounts per market
/// @return buyInAmountInDefaultCollateral buy-in amount in default collateral
/// @return riskStatus risk status
function tradeQuote(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
address _collateral,
bool _isLive
)
external
view
returns (
uint totalQuote,
uint payout,
uint fees,
uint[] memory amountsToBuy,
uint buyInAmountInDefaultCollateral,
ISportsAMMV2RiskManager.RiskStatus riskStatus
)
{
(totalQuote, payout, fees, amountsToBuy, buyInAmountInDefaultCollateral, riskStatus) = _tradeQuoteCommon(
_tradeData,
_buyInAmount,
_collateral,
_isLive,
NON_SYSTEM_BET
);
}
/// @notice gets trade quote
/// @param _tradeData trade data with all market info needed for ticket
/// @param _buyInAmount ticket buy-in amount
/// @param _collateral different collateral used for payment
/// @param _isLive whether this is a live bet
/// @param _systemBetDenominator the denominator for system bets
/// @return totalQuote total ticket quote
/// @return payout expected payout
/// @return fees ticket fees
/// @return amountsToBuy amounts per market
/// @return buyInAmountInDefaultCollateral buy-in amount in default collateral
/// @return riskStatus risk status
function tradeQuoteSystem(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
address _collateral,
bool _isLive,
uint8 _systemBetDenominator
)
external
view
returns (
uint totalQuote,
uint payout,
uint fees,
uint[] memory amountsToBuy,
uint buyInAmountInDefaultCollateral,
ISportsAMMV2RiskManager.RiskStatus riskStatus
)
{
(totalQuote, payout, fees, amountsToBuy, buyInAmountInDefaultCollateral, riskStatus) = _tradeQuoteCommon(
_tradeData,
_buyInAmount,
_collateral,
_isLive,
_systemBetDenominator
);
}
function _tradeQuoteCommon(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
address _collateral,
bool _isLive,
uint8 _systemBetDenominator
)
internal
view
returns (
uint totalQuote,
uint payout,
uint fees,
uint[] memory amountsToBuy,
uint buyInAmountInDefaultCollateral,
ISportsAMMV2RiskManager.RiskStatus riskStatus
)
{
uint useAmount = _buyInAmount;
buyInAmountInDefaultCollateral = _buyInAmount;
if (_collateral != address(0) && _collateral != address(defaultCollateral)) {
address liqPoolToUse = liquidityPoolForCollateral[_collateral];
if (liqPoolToUse == address(0)) {
buyInAmountInDefaultCollateral = multiCollateralOnOffRamp.getMinimumReceived(_collateral, _buyInAmount);
useAmount = buyInAmountInDefaultCollateral;
} else {
buyInAmountInDefaultCollateral = _transformToUSD(
_buyInAmount,
ISportsAMMV2LiquidityPool(liqPoolToUse).getCollateralPrice(),
ISportsAMMV2Manager(_collateral).decimals()
);
}
}
if (useAmount == 0) revert ZeroAmount();
(totalQuote, payout, fees, amountsToBuy, riskStatus) = _tradeQuote(
_tradeData,
TradeDataQuoteInternal(useAmount, true, buyInAmountInDefaultCollateral, _collateral, _isLive, false, 0),
_systemBetDenominator
);
}
/* ========== EXTERNAL WRITE FUNCTIONS ========== */
/// @notice make a trade and create a ticket
/// @param _tradeData trade data with all market info needed for ticket
/// @param _buyInAmount ticket buy-in amount
/// @param _expectedQuote expected payout got from quote method
/// @param _additionalSlippage slippage tolerance
/// @param _referrer referrer to get referral fee
/// @param _collateral different collateral used for payment
/// @param _isEth pay with ETH
/// @return _createdTicket the address of the created ticket
function trade(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral,
bool _isEth
) external payable nonReentrant notPaused returns (address _createdTicket) {
_createdTicket = _tradeInternal(
_tradeData,
TradeTypeData(NON_SYSTEM_BET, false, false),
_buyInAmount,
_expectedQuote,
_additionalSlippage,
_referrer,
_collateral,
_isEth,
msg.sender
);
}
/// @notice make a SGP trade and create a ticket
/// @param _tradeData trade data with all market info needed for ticket
/// @param _buyInAmount ticket buy-in amount
/// @param _approvedQuote quote approved by sgpTradingProcessor
/// @param _referrer referrer to get referral fee
/// @param _collateral different collateral used for payment
/// @return _createdTicket the address of the created ticket
function tradeSGP(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _approvedQuote,
address _recipient,
address _referrer,
address _collateral
) external payable nonReentrant notPaused onlyValidRecipient(_recipient) returns (address _createdTicket) {
if (msg.sender != sgpTradingProcessor) revert OnlyDedicatedProcessor();
_createdTicket = _tradeInternal(
_tradeData,
TradeTypeData(NON_SYSTEM_BET, true, false),
_buyInAmount,
_approvedQuote,
0, //no additional slippage as quote is assigned by CL node
_referrer,
_collateral,
false,
_recipient
);
}
/// @notice make a trade and create a ticket
/// @param _tradeData trade data with all market info needed for ticket
/// @param _buyInAmount ticket buy-in amount
/// @param _expectedQuote expected payout got from quote method
/// @param _additionalSlippage slippage tolerance
/// @param _referrer referrer to get referral fee
/// @param _collateral different collateral used for payment
/// @param _isEth pay with ETH
/// @param _systemBetDenominator minimum number of winning bets for a system bet
/// @return _createdTicket the address of the created ticket
function tradeSystemBet(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral,
bool _isEth,
uint8 _systemBetDenominator
) external payable nonReentrant notPaused returns (address _createdTicket) {
if (_systemBetDenominator <= 1 || _systemBetDenominator >= _tradeData.length) revert SBDOutOfRange();
_createdTicket = _tradeInternal(
_tradeData,
TradeTypeData(_systemBetDenominator, false, false),
_buyInAmount,
_expectedQuote,
_additionalSlippage,
_referrer,
_collateral,
_isEth,
msg.sender
);
}
/// @notice make a live trade and create a ticket
/// @param _tradeData trade data with all market info needed for ticket
/// @param _buyInAmount ticket buy-in amount
/// @param _expectedQuote expected payout got from LiveTradingProcessor method
/// @param _recipient different recipient of the ticket
/// @param _referrer referrer to get referral fee
/// @param _collateral different collateral used for payment
/// @return _createdTicket the address of the created ticket
function tradeLive(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
address _recipient,
address _referrer,
address _collateral
) external nonReentrant notPaused onlyValidRecipient(_recipient) returns (address _createdTicket) {
if (msg.sender != liveTradingProcessor) revert OnlyDedicatedProcessor();
_createdTicket = _tradeInternal(
_tradeData,
TradeTypeData(NON_SYSTEM_BET, false, true),
_buyInAmount,
_expectedQuote,
0, //no additional slippage as quote is assigned by CL node
_referrer,
_collateral,
false,
_recipient
);
}
function _tradeInternal(
ISportsAMMV2.TradeData[] memory _tradeData,
TradeTypeData memory _tradeTypeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral,
bool _isEth,
address _recipient
) internal returns (address _createdTicket) {
if (_expectedQuote == 0 || _buyInAmount == 0) revert IllegalInputAmounts();
_setReferrer(_referrer, _recipient);
address useLPpool;
uint collateralPriceInUSD;
(useLPpool, collateralPriceInUSD, _buyInAmount, _collateral) = _handleCollateral(
_buyInAmount,
_collateral,
_recipient,
_isEth
);
_createdTicket = _trade(
_tradeData,
TradeDataInternal(
_buyInAmount,
_divWithDecimals(_buyInAmount, _expectedQuote), // quote to expected payout
_additionalSlippage,
_recipient,
_tradeTypeData._isLive,
_collateral,
useLPpool,
collateralPriceInUSD,
_tradeTypeData._isSGP
),
_tradeTypeData._systemBetDenominator
);
}
/**
* @notice Resolves a ticket by exercising, canceling, or marking it as lost.
* @dev
* - Anyone can call this to exercise a ticket.
* - Only addresses whitelisted as MARKET_RESOLVING can cancel or mark a ticket as lost.
* - Uses internal _exerciseTicket logic with the appropriate flags.
* @param _ticket The address of the ticket to be resolved.
* @param action The type of resolution action to perform:
* - TicketAction.Exercise: Exercise a resolved ticket (no whitelist required)
* - TicketAction.Cancel: Cancel the ticket (whitelist required)
* - TicketAction.MarkLost: Mark the ticket as lost (whitelist required)
*/
function handleTicketResolving(
address _ticket,
ISportsAMMV2.TicketAction action
) external nonReentrant notPaused onlyKnownTickets(_ticket) {
if (action != ISportsAMMV2.TicketAction.Exercise) {
if (!manager.isWhitelistedAddress(msg.sender, ISportsAMMV2Manager.Role.MARKET_RESOLVING)) {
revert UnsupportedSender();
}
}
if (action == ISportsAMMV2.TicketAction.Cancel) {
_exerciseTicket(_ticket, address(0), true, false);
} else if (action == ISportsAMMV2.TicketAction.MarkLost) {
_exerciseTicket(_ticket, address(0), false, true);
} else {
_exerciseTicket(_ticket, address(0), false, false);
}
}
/// @notice exercise specific ticket to an off ramp collateral
/// @param _ticket ticket address
/// @param _exerciseCollateral collateral address to off ramp to
function exerciseTicketOffRamp(
address _ticket,
address _exerciseCollateral
) external nonReentrant notPaused onlyKnownTickets(_ticket) {
if (msg.sender != Ticket(_ticket).ticketOwner()) revert OnlyTicketOwner();
_exerciseTicket(_ticket, _exerciseCollateral, false, false);
}
/// @notice Withdraws collateral from a specified Ticket contract and sends it to the target address
/// @param ticketAddress The address of the Ticket contract
/// @param recipient The address to receive the withdrawn collateral
function withdrawCollateralFromTicket(address ticketAddress, address recipient) external onlyOwner {
// Call withdrawCollateral on the specified Ticket contract
Ticket(ticketAddress).withdrawCollateral(recipient);
}
/// @notice expire provided tickets
/// @param _tickets array of tickets to be expired
function expireTickets(address[] calldata _tickets) external onlyOwner {
unchecked {
for (uint i; i < _tickets.length; ++i) {
address ticketAddress = _tickets[i];
address ticketOwner = Ticket(ticketAddress).ticketOwner();
if (ticketOwner != freeBetsHolder && ticketOwner != stakingThalesBettingProxy) {
Ticket(ticketAddress).expire(msg.sender);
manager.expireKnownTicket(ticketAddress, ticketOwner);
}
}
}
}
/* ========== INTERNAL FUNCTIONS ========== */
function _tradeQuote(
ISportsAMMV2.TradeData[] memory _tradeData,
TradeDataQuoteInternal memory _tradeDataQuoteInternal,
uint8 _systemBetDenominator
)
internal
view
returns (
uint totalQuote,
uint payout,
uint fees,
uint[] memory amountsToBuy,
ISportsAMMV2RiskManager.RiskStatus riskStatus
)
{
uint numOfMarkets = _tradeData.length;
amountsToBuy = new uint[](numOfMarkets);
uint maxSupportedOdds = riskManager.maxSupportedOdds();
bool isSystemBet = _systemBetDenominator > 1;
uint addedPayoutPercentage = addedPayoutPercentagePerCollateral[_tradeDataQuoteInternal._collateral];
for (uint i; i < numOfMarkets; ++i) {
ISportsAMMV2.TradeData memory marketTradeData = _tradeData[i];
riskManager.verifyMerkleTree(marketTradeData, rootPerGame[marketTradeData.gameId]);
if (marketTradeData.odds.length <= marketTradeData.position) revert InvalidPosition();
uint marketOdds = marketTradeData.odds[marketTradeData.position];
marketOdds =
(marketOdds * ONE) /
((ONE + addedPayoutPercentage) - _mulWithDecimals(addedPayoutPercentage, marketOdds));
amountsToBuy[i] =
_divWithDecimals(_tradeDataQuoteInternal._buyInAmount, marketOdds) -
_tradeDataQuoteInternal._buyInAmount;
if (isSystemBet) {
amountsToBuy[i] = (amountsToBuy[i] * ONE * _systemBetDenominator) / (numOfMarkets * ONE);
}
// amounts to buy should be decreased by buyinamount
totalQuote = totalQuote == 0 ? marketOdds : _mulWithDecimals(totalQuote, marketOdds);
}
if (totalQuote != 0) {
if (isSystemBet) {
(payout, totalQuote) = riskManager.getMaxSystemBetPayout(
_tradeData,
_systemBetDenominator,
_tradeDataQuoteInternal._buyInAmount,
addedPayoutPercentage
);
} else {
if (_tradeDataQuoteInternal._isSGP) {
totalQuote = _tradeDataQuoteInternal._approvedQuote;
totalQuote =
(totalQuote * ONE) /
((ONE + addedPayoutPercentage) - _mulWithDecimals(addedPayoutPercentage, totalQuote));
}
payout = _divWithDecimals(_tradeDataQuoteInternal._buyInAmount, totalQuote);
}
if (totalQuote < maxSupportedOdds) {
totalQuote = maxSupportedOdds;
payout = _divWithDecimals(_tradeDataQuoteInternal._buyInAmount, totalQuote);
}
fees = _getFees(_tradeDataQuoteInternal._buyInAmount);
if (_tradeDataQuoteInternal._shouldCheckRisks) {
bool[] memory isMarketOutOfLiquidity;
(riskStatus, isMarketOutOfLiquidity) = riskManager.checkRisks(
_tradeData,
_tradeDataQuoteInternal._buyInAmountInDefaultCollateral,
_tradeDataQuoteInternal._isLive,
_systemBetDenominator
);
unchecked {
for (uint i; i < numOfMarkets; ++i) {
if (isMarketOutOfLiquidity[i]) amountsToBuy[i] = 0;
}
}
if (riskStatus != ISportsAMMV2RiskManager.RiskStatus.NoRisk) {
totalQuote = payout = 0;
}
}
}
}
function _handleCollateral(
uint _buyInAmount,
address _collateral,
address _fromAddress,
bool _isEth
) internal returns (address lqPool, uint collateralPrice, uint buyInAmount, address collateralAfterOnramp) {
buyInAmount = _buyInAmount;
// Default collateral path (including address(0) case)
if (_collateral == address(0) || _collateral == address(defaultCollateral)) {
collateralAfterOnramp = address(defaultCollateral);
defaultCollateral.safeTransferFrom(_fromAddress, address(this), _buyInAmount);
}
// Non-default collateral path
else {
collateralAfterOnramp = _collateral;
// Handle ETH or ERC20 transfer
if (_isEth) {
if (_collateral != multiCollateralOnOffRamp.WETH9() || msg.value < _buyInAmount) revert InsuffETHSent();
IWeth(_collateral).deposit{value: msg.value}();
} else {
IERC20(_collateral).safeTransferFrom(_fromAddress, address(this), _buyInAmount);
}
// Check if direct liquidity pool exists for collateral
lqPool = liquidityPoolForCollateral[_collateral];
if (lqPool != address(0)) {
collateralPrice = ISportsAMMV2LiquidityPool(lqPool).getCollateralPrice();
if (collateralPrice == 0) revert ZeroPriceForCollateral();
}
// Handle onramping if no direct pool
else {
if (address(multiCollateralOnOffRamp) == address(0)) revert MultiCollatDisabled();
IERC20(_collateral).approve(address(multiCollateralOnOffRamp), _buyInAmount);
buyInAmount = multiCollateralOnOffRamp.onramp(_collateral, _buyInAmount);
if (buyInAmount < multiCollateralOnOffRamp.getMinimumReceived(_collateral, _buyInAmount))
revert InsuffReceived();
collateralAfterOnramp = address(defaultCollateral);
}
}
// Get final liquidity pool
lqPool = liquidityPoolForCollateral[collateralAfterOnramp];
}
function _trade(
ISportsAMMV2.TradeData[] memory _tradeData,
TradeDataInternal memory _tradeDataInternal,
uint8 _systemBetDenominator
) internal returns (address) {
TradeProcessingParams memory processingParams;
processingParams._addedPayoutPercentage = addedPayoutPercentagePerCollateral[_tradeDataInternal._collateral];
if (!_tradeDataInternal._isLive) {
(processingParams._totalQuote, processingParams._payout, processingParams._fees, , ) = _tradeQuote(
_tradeData,
TradeDataQuoteInternal(
_tradeDataInternal._buyInAmount,
false,
0,
_tradeDataInternal._collateral,
_tradeDataInternal._isLive,
_tradeDataInternal._isSGP,
_divWithDecimals(_tradeDataInternal._buyInAmount, _tradeDataInternal._expectedPayout)
),
_systemBetDenominator
);
} else {
processingParams._totalQuote = _divWithDecimals(
_tradeDataInternal._buyInAmount,
_tradeDataInternal._expectedPayout
);
processingParams._totalQuote =
(processingParams._totalQuote * ONE) /
((ONE + processingParams._addedPayoutPercentage) -
_mulWithDecimals(processingParams._addedPayoutPercentage, processingParams._totalQuote));
processingParams._payout = _divWithDecimals(_tradeDataInternal._buyInAmount, processingParams._totalQuote);
processingParams._fees = _getFees(_tradeDataInternal._buyInAmount);
}
processingParams._payoutWithFees = processingParams._payout + processingParams._fees;
checkRisksLimits(
_tradeData,
processingParams._totalQuote,
processingParams._payout,
_tradeDataInternal,
_systemBetDenominator
);
// Clone a ticket
Ticket.MarketData[] memory markets = _getTicketMarkets(_tradeData, processingParams._addedPayoutPercentage);
Ticket ticket = Ticket(Clones.clone(ticketMastercopy));
// 1) Initialize the ticket (unchanged)
ticket.initialize(
Ticket.TicketInit(
markets,
_tradeDataInternal._buyInAmount,
processingParams._fees,
processingParams._totalQuote,
address(this),
_tradeDataInternal._recipient,
IERC20(_tradeDataInternal._collateral),
(block.timestamp + riskManager.expiryDuration()),
_tradeDataInternal._isLive,
_systemBetDenominator,
_tradeDataInternal._isSGP
)
);
// 2) Track ticket on the manager (unchanged)
manager.addNewKnownTicket(_tradeData, address(ticket), _tradeDataInternal._recipient);
// 3) Commit trade to LP (unchanged)
ISportsAMMV2LiquidityPool(_tradeDataInternal._collateralPool).commitTrade(
address(ticket),
processingParams._payoutWithFees - _tradeDataInternal._buyInAmount
);
// 4) Fund the ticket with the full expected amount (unchanged)
IERC20(_tradeDataInternal._collateral).safeTransfer(address(ticket), processingParams._payoutWithFees);
// 5) Lock the accounting: tell the Ticket what the authoritative funded amount is
ticket.setExpectedFinalPayout(processingParams._payoutWithFees);
emit NewTicket(
markets,
address(ticket),
_tradeDataInternal._buyInAmount,
processingParams._payout,
_tradeDataInternal._isLive
);
emit TicketCreated(
address(ticket),
_tradeDataInternal._recipient,
_tradeDataInternal._buyInAmount,
processingParams._fees,
processingParams._payout,
processingParams._totalQuote,
_tradeDataInternal._collateral
);
return address(ticket);
}
// Checks risk and updates Staking Volume
function checkRisksLimits(
ISportsAMMV2.TradeData[] memory _tradeData,
uint _totalQuote,
uint _payout,
TradeDataInternal memory _tradeDataInternal,
uint8 _systemBetDenominator
) internal {
uint _buyInAmount = _tradeDataInternal._buyInAmount;
uint _collateralPriceInUSD = _tradeDataInternal._collateralPriceInUSD;
uint _expectedPayout = _tradeDataInternal._expectedPayout;
if (_collateralPriceInUSD > 0) {
uint collateralDecimals = ISportsAMMV2Manager(_tradeDataInternal._collateral).decimals();
_buyInAmount = _transformToUSD(_buyInAmount, _collateralPriceInUSD, collateralDecimals);
_payout = _transformToUSD(_payout, _collateralPriceInUSD, collateralDecimals);
_expectedPayout = _transformToUSD(_expectedPayout, _collateralPriceInUSD, collateralDecimals);
}
riskManager.checkAndUpdateRisks(
_tradeData,
_buyInAmount,
_payout,
_tradeDataInternal._isLive,
_systemBetDenominator,
_tradeDataInternal._isSGP
);
riskManager.checkLimits(
_buyInAmount,
_totalQuote,
_payout,
_expectedPayout,
_tradeDataInternal._additionalSlippage,
_tradeData.length
);
}
function _getTicketMarkets(
ISportsAMMV2.TradeData[] memory _tradeData,
uint _addedPayoutPercentage
) internal pure returns (Ticket.MarketData[] memory markets) {
uint len = _tradeData.length;
markets = new Ticket.MarketData[](len);
for (uint i; i < len; ++i) {
ISportsAMMV2.TradeData memory marketTradeData = _tradeData[i];
uint odds = marketTradeData.odds[marketTradeData.position];
markets[i] = Ticket.MarketData(
marketTradeData.gameId,
marketTradeData.sportId,
marketTradeData.typeId,
marketTradeData.maturity,
marketTradeData.status,
marketTradeData.line,
marketTradeData.playerId,
marketTradeData.position,
(odds * ONE) / ((ONE + _addedPayoutPercentage) - _mulWithDecimals(_addedPayoutPercentage, odds)),
marketTradeData.combinedPositions[marketTradeData.position]
);
}
}
function _handleFees(uint _buyInAmount, address _tickerOwner, IERC20 _collateral) internal returns (uint fees) {
uint referrerShare;
address referrer = referrals.sportReferrals(_tickerOwner);
uint ammBalance = _collateral.balanceOf(address(this));
if (referrer != address(0) && _tickerOwner != address(freeBetsHolder)) {
uint referrerFeeByTier = referrals.getReferrerFee(referrer);
if (referrerFeeByTier > 0) {
referrerShare = _mulWithDecimals(_buyInAmount, referrerFeeByTier);
if (ammBalance >= referrerShare) {
_collateral.safeTransfer(referrer, referrerShare);
emit ReferrerPaid(referrer, _tickerOwner, referrerShare, _buyInAmount, address(_collateral));
ammBalance -= referrerShare;
}
}
}
fees = _getFees(_buyInAmount);
if (fees > referrerShare) {
uint safeBoxAmount = fees - referrerShare;
if (ammBalance >= safeBoxAmount) {
address _safeBoxPerCollateral = safeBoxPerCollateral[address(_collateral)];
_collateral.safeTransfer(
_safeBoxPerCollateral != address(0) ? _safeBoxPerCollateral : safeBox,
safeBoxAmount
);
emit SafeBoxFeePaid(safeBoxFee, safeBoxAmount, address(_collateral));
}
}
}
// Transform collateral to USD
function _transformToUSD(
uint _amountInCollateral,
uint _collateralPriceInUSD,
uint _collateralDecimals
) internal view returns (uint amountInUSD) {
amountInUSD = _mulWithDecimals(_amountInCollateral, _collateralPriceInUSD);
if (_collateralDecimals < defaultCollateralDecimals) {
amountInUSD = amountInUSD * 10 ** (defaultCollateralDecimals - _collateralDecimals);
} else if (_collateralDecimals > defaultCollateralDecimals) {
amountInUSD = amountInUSD / 10 ** (_collateralDecimals - defaultCollateralDecimals);
}
}
function _getFees(uint _buyInAmount) internal view returns (uint) {
return (_buyInAmount * safeBoxFee) / ONE;
}
function _divWithDecimals(uint _dividend, uint _divisor) internal pure returns (uint) {
return (ONE * _dividend) / _divisor;
}
function _mulWithDecimals(uint _firstMul, uint _secondMul) internal pure returns (uint) {
return (_firstMul * _secondMul) / ONE;
}
function _setReferrer(address _referrer, address _recipient) internal {
if (_referrer != address(0) && _recipient != address(freeBetsHolder)) referrals.setReferrer(_referrer, _recipient);
}
function _exerciseTicket(address _ticket, address _exerciseCollateral, bool _cancelTicket, bool _markLost) internal {
Ticket ticket = Ticket(_ticket);
uint userWonAmount;
if (_markLost) {
userWonAmount = ticket.markAsLost();
} else if (_cancelTicket) {
userWonAmount = ticket.cancel();
} else {
userWonAmount = ticket.exercise(_exerciseCollateral);
}
IERC20 ticketCollateral = ticket.collateral();
address ticketOwner = ticket.ticketOwner();
if (ticketOwner == freeBetsHolder || ticketOwner == stakingThalesBettingProxy) {
IProxyBetting(ticketOwner).confirmTicketResolved(_ticket);
}
if (!ticket.cancelled()) {
_handleFees(ticket.buyInAmount(), ticketOwner, ticketCollateral);
}
manager.resolveKnownTicket(_ticket, ticketOwner);
emit TicketResolved(_ticket, ticketOwner, ticket.isUserTheWinner());
if (userWonAmount > 0 && _exerciseCollateral != address(0) && _exerciseCollateral != address(ticketCollateral)) {
if (ticketCollateral != defaultCollateral) revert OfframpOnlyDefaultCollateralAllowed();
IERC20(_exerciseCollateral).safeTransfer(
ticketOwner,
multiCollateralOnOffRamp.offramp(_exerciseCollateral, userWonAmount)
);
}
// mark ticket as exercised in LiquidityPool and return any funds to the pool if ticket was lost or cancelled
ISportsAMMV2LiquidityPool(liquidityPoolForCollateral[address(ticketCollateral)]).transferToPool(
_ticket,
ticketCollateral.balanceOf(address(this))
);
}
/* ========== SETTERS ========== */
/// @notice set roots of merkle tree
/// @param _games game IDs
/// @param _roots new roots
function setRootsPerGames(bytes32[] memory _games, bytes32[] memory _roots) external onlyWhitelistedAddresses {
if (_games.length != _roots.length) revert InvalidLength();
unchecked {
for (uint i; i < _games.length; ++i) {
_setRootForGame(_games[i], _roots[i]);
}
}
}
/// @notice set root of merkle tree
/// @param _game game ID
/// @param _root new root
function setRootForGame(bytes32 _game, bytes32 _root) external onlyWhitelistedAddresses {
_setRootForGame(_game, _root);
}
function _setRootForGame(bytes32 _game, bytes32 _root) internal {
rootPerGame[_game] = _root;
emit GameRootUpdated(_game, _root);
}
/// @notice sets main addresses
/// @param _defaultCollateral the default token used for payment
/// @param _manager manager address
/// @param _riskManager risk manager address
/// @param _resultManager result manager address
/// @param _referrals referrals address
/// @param _safeBox safeBox address
function setAddresses(
IERC20 _defaultCollateral,
address _manager,
address _riskManager,
address _resultManager,
address _referrals,
address _safeBox
) external onlyOwner {
defaultCollateral = _defaultCollateral;
defaultCollateralDecimals = ISportsAMMV2Manager(address(defaultCollateral)).decimals();
manager = ISportsAMMV2Manager(_manager);
riskManager = ISportsAMMV2RiskManager(_riskManager);
resultManager = ISportsAMMV2ResultManager(_resultManager);
referrals = IReferrals(_referrals);
safeBox = _safeBox;
emit AddressesUpdated(_defaultCollateral, _manager, _riskManager, _resultManager, _referrals, _safeBox);
}
/**
* @notice Sets the addresses of various betting processors.
* @dev This function can only be called by the contract owner.
* @param _liveTradingProcessor Address of the live trading processor contract.
* @param _sgpTradingProcessor Address of the single-game parlay trading processor contract.
* @param _freeBetsHolder Address of the free bets holder contract.
*/
function setBettingProcessors(
address _liveTradingProcessor,
address _sgpTradingProcessor,
address _freeBetsHolder
) external onlyOwner {
liveTradingProcessor = _liveTradingProcessor;
sgpTradingProcessor = _sgpTradingProcessor;
freeBetsHolder = _freeBetsHolder;
emit SetBettingProcessors(liveTradingProcessor, sgpTradingProcessor, freeBetsHolder);
}
/// @notice sets new Ticket Mastercopy address
/// @param _ticketMastercopy new Ticket Mastercopy address
function setTicketMastercopy(address _ticketMastercopy) external onlyOwner {
ticketMastercopy = _ticketMastercopy;
emit TicketMastercopyUpdated(_ticketMastercopy);
}
/// @notice sets multi-collateral on/off ramp contract and enable/disable
/// @param _onOffRamper new multi-collateral on/off ramp address
function setMultiCollateralOnOffRamp(address _onOffRamper) external onlyOwner {
_updateApproval(defaultCollateral, address(multiCollateralOnOffRamp), _onOffRamper);
multiCollateralOnOffRamp = IMultiCollateralOnOffRamp(_onOffRamper);
emit SetMultiCollateralOnOffRamp(_onOffRamper);
}
/// @notice sets different amounts
/// @param _safeBoxFee safe box fee paid on each trade
function setAmounts(uint _safeBoxFee) external onlyOwner {
safeBoxFee = _safeBoxFee;
emit AmountsUpdated(_safeBoxFee);
}
/// @notice Sets parameters related to a collateral asset.
/// @dev Only updates fields if the new value differs from the stored one.
/// @param _collateral The collateral token address.
/// @param _liquidityPool New liquidity pool address (optional, skip if same).
/// @param _addedPayout New added payout percentage (optional, skip if same).
/// @param _safeBox New dedicated SafeBox address (optional, skip if same).
function configureCollateral(
address _collateral,
address _liquidityPool,
uint _addedPayout,
address _safeBox
) external onlyOwner {
// Liquidity pool update
if (liquidityPoolForCollateral[_collateral] != _liquidityPool) {
_updateApproval(IERC20(_collateral), liquidityPoolForCollateral[_collateral], _liquidityPool);
liquidityPoolForCollateral[_collateral] = _liquidityPool;
}
// Added payout percentage update
addedPayoutPercentagePerCollateral[_collateral] = _addedPayout;
// SafeBox override update
safeBoxPerCollateral[_collateral] = _safeBox;
emit CollateralConfigured(_collateral, _liquidityPool, _addedPayout, _safeBox);
}
function _updateApproval(IERC20 token, address oldSpender, address newSpender) internal {
if (oldSpender != address(0)) {
token.approve(oldSpender, 0);
}
if (newSpender != address(0)) {
token.approve(newSpender, MAX_APPROVAL);
}
}
/* ========== MODIFIERS ========== */
modifier onlyKnownTickets(address _ticket) {
if (!manager.isKnownTicket(_ticket)) revert UnknownTicket();
_;
}
modifier onlyWhitelistedAddresses() {
if (!manager.isWhitelistedAddress(msg.sender, ISportsAMMV2Manager.Role.ROOT_SETTING) && msg.sender != owner)
revert InvalidSender();
_;
}
modifier onlyValidRecipient(address _recipient) {
if (_recipient == address(0)) revert UndefinedRecipient();
_;
}
/* ========== EVENTS ========== */
event NewTicket(Ticket.MarketData[] markets, address ticket, uint buyInAmount, uint payout, bool isLive);
event TicketCreated(
address ticket,
address recipient,
uint buyInAmount,
uint fees,
uint payout,
uint totalQuote,
address collateral
);
event TicketResolved(address ticket, address ticketOwner, bool isUserTheWinner);
event ReferrerPaid(address refferer, address trader, uint amount, uint volume, address collateral);
event SafeBoxFeePaid(uint safeBoxFee, uint safeBoxAmount, address collateral);
event GameRootUpdated(bytes32 game, bytes32 root);
event AmountsUpdated(uint safeBoxFee);
event AddressesUpdated(
IERC20 defaultCollateral,
address manager,
address riskManager,
address resultManager,
address referrals,
address safeBox
);
event TicketMastercopyUpdated(address ticketMastercopy);
event SetMultiCollateralOnOffRamp(address onOffRamper);
event SetBettingProcessors(address liveTradingProcessor, address sgpTradingProcessor, address freeBetsHolder);
event CollateralConfigured(address collateral, address liquidityPool, uint addedPayout, address safeBox);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @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 proxied contracts do not make use of 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.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* 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 prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol)
pragma solidity ^0.8.20;
/**
* @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.
*/
library Clones {
/**
* @dev A clone instance deployment failed.
*/
error ERC1167FailedCreateClone();
/**
* @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 {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(0, 0x09, 0x37)
}
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
/**
* @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 {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(0, 0x09, 0x37, salt)
}
if (instance == address(0)) {
revert ERC1167FailedCreateClone();
}
}
/**
* @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(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := keccak256(add(ptr, 0x43), 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 (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
interface IMultiCollateralOnOffRamp {
function onramp(address collateral, uint collateralAmount) external returns (uint);
function onrampWithEth(uint amount) external payable returns (uint);
function getMinimumReceived(address collateral, uint amount) external view returns (uint);
function getMinimumNeeded(address collateral, uint amount) external view returns (uint);
function WETH9() external view returns (address);
function offrampIntoEth(uint amount) external returns (uint);
function offramp(address collateral, uint amount) external returns (uint);
function offrampFromIntoEth(address collateralFrom, uint amount) external returns (uint);
function offrampFrom(
address collateralFrom,
address collateralTo,
uint amount
) external returns (uint);
function priceFeed() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
interface IPriceFeed {
// Structs
struct RateAndUpdatedTime {
uint216 rate;
uint40 time;
}
// Mutative functions
function addAggregator(bytes32 currencyKey, address aggregatorAddress) external;
function removeAggregator(bytes32 currencyKey) external;
// Views
function rateForCurrency(bytes32 currencyKey) external view returns (uint);
function rateAndUpdatedTime(bytes32 currencyKey) external view returns (uint rate, uint time);
function getRates() external view returns (uint[] memory);
function getCurrencies() external view returns (bytes32[] memory);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
interface IReferrals {
function referrals(address) external view returns (address);
function getReferrerFee(address) external view returns (uint);
function sportReferrals(address) external view returns (address);
function setReferrer(address, address) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
interface IStakingThales {
function updateVolume(address account, uint amount) external;
function updateStakingRewards(
uint _currentPeriodRewards,
uint _extraRewards,
uint _revShare
) external;
/* ========== VIEWS / VARIABLES ========== */
function totalStakedAmount() external view returns (uint);
function stakedBalanceOf(address account) external view returns (uint);
function currentPeriodRewards() external view returns (uint);
function currentPeriodFees() external view returns (uint);
function getLastPeriodOfClaimedRewards(address account) external view returns (uint);
function getRewardsAvailable(address account) external view returns (uint);
function getRewardFeesAvailable(address account) external view returns (uint);
function getAlreadyClaimedRewards(address account) external view returns (uint);
function getContractRewardFunds() external view returns (uint);
function getContractFeeFunds() external view returns (uint);
function getAMMVolume(address account) external view returns (uint);
function decreaseAndTransferStakedThales(address account, uint amount) external;
function increaseAndTransferStakedThales(address account, uint amount) external;
function updateVolumeAtAmountDecimals(
address account,
uint amount,
uint decimals
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// internal
import "../../interfaces/ISportsAMMV2Manager.sol";
import "../../interfaces/ISportsAMMV2.sol";
contract Ticket {
using SafeERC20 for IERC20;
uint private constant ONE = 1e18;
enum Phase {
Trading,
Maturity,
Expiry
}
struct MarketData {
bytes32 gameId;
uint16 sportId;
uint16 typeId;
uint maturity;
uint8 status;
int24 line;
uint24 playerId;
uint8 position;
uint odd;
ISportsAMMV2.CombinedPosition[] combinedPositions;
}
struct TicketInit {
MarketData[] _markets;
uint _buyInAmount;
uint _fees;
uint _totalQuote;
address _sportsAMM;
address _ticketOwner;
IERC20 _collateral;
uint _expiry;
bool _isLive;
uint8 _systemBetDenominator;
bool _isSGP;
}
ISportsAMMV2 public sportsAMM;
address public ticketOwner;
IERC20 public collateral;
uint public buyInAmount;
uint public fees;
uint public totalQuote;
uint public numOfMarkets;
uint public expiry;
uint public createdAt;
bool public resolved;
bool public paused;
bool public initialized;
bool public cancelled;
bool public isLive;
mapping(uint => MarketData) public markets;
uint public finalPayout;
bool public isSystem;
uint8 public systemBetDenominator;
bool public isSGP;
bool public isMarkedAsLost;
uint public expectedFinalPayout;
/* ========== CONSTRUCTOR and INITIALIZERS========== */
/// @notice initialize the ticket contract
/// @param params all parameters for Init
function initialize(TicketInit calldata params) external {
require(!initialized, "Ticket already initialized");
initialized = true;
sportsAMM = ISportsAMMV2(params._sportsAMM);
numOfMarkets = params._markets.length;
for (uint i = 0; i < numOfMarkets; i++) {
markets[i] = params._markets[i];
}
buyInAmount = params._buyInAmount;
fees = params._fees;
totalQuote = params._totalQuote;
ticketOwner = params._ticketOwner;
collateral = params._collateral;
expiry = params._expiry;
isLive = params._isLive;
createdAt = block.timestamp;
systemBetDenominator = params._systemBetDenominator;
isSystem = systemBetDenominator > 0;
isSGP = params._isSGP;
}
/**
* @notice Sets the expected final payout amount for this ticket.
* @dev
* - Can only be called by the SportsAMM contract.
* - This value represents the total amount of collateral (including fees)
* that was initially funded to the ticket upon creation.
* - Used later in `exercise()` to prevent manipulation or overfunding attacks,
* ensuring payout calculations rely only on the original committed collateral
* and not on the current token balance of the contract.
* - Once set, this value should remain constant throughout the ticket lifecycle.
*
* @param amount The total expected collateral amount that should be held by this ticket.
* Must include both user buy-in and fees.
*
* Emits a {ExpectedFinalPayoutSet} event.
*/
function setExpectedFinalPayout(uint amount) external onlyAMM {
expectedFinalPayout = amount;
emit ExpectedFinalPayoutSet(amount);
}
/* ========== EXTERNAL READ FUNCTIONS ========== */
/// @notice checks if the user lost the ticket
/// @return isTicketLost true/false
function isTicketLost() public view returns (bool) {
if (isMarkedAsLost) {
return true;
} else {
uint lostMarketsCount = 0;
for (uint i = 0; i < numOfMarkets; i++) {
(bool isMarketResolved, bool isWinningMarketPosition) = sportsAMM
.resultManager()
.isMarketResolvedAndPositionWinning(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
if (isMarketResolved && !isWinningMarketPosition) {
if (!isSystem) {
return true;
} else {
lostMarketsCount++;
if (lostMarketsCount > (numOfMarkets - systemBetDenominator)) {
return true;
}
}
}
}
return false;
}
}
/// @notice checks are all markets of the ticket resolved
/// @return areAllMarketsResolved true/false
function areAllMarketsResolved() public view returns (bool) {
for (uint i = 0; i < numOfMarkets; i++) {
if (
!sportsAMM.resultManager().isMarketResolved(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].combinedPositions
)
) {
return false;
}
}
return true;
}
/// @notice checks if the user won the ticket
/// @return hasUserWon true/false
function isUserTheWinner() external view returns (bool hasUserWon) {
hasUserWon = _isUserTheWinner();
}
/// @notice checks if the ticket ready to be exercised
/// @return isExercisable true/false
function isTicketExercisable() public view returns (bool isExercisable) {
isExercisable = !resolved && (areAllMarketsResolved() || isTicketLost());
}
/// @notice gets current phase of the ticket
/// @return phase ticket phase
function phase() public view returns (Phase) {
return
isTicketExercisable() || resolved ? ((expiry < block.timestamp) ? Phase.Expiry : Phase.Maturity) : Phase.Trading;
}
/// @notice gets combined positions of the game
/// @return combinedPositions game combined positions
function getCombinedPositions(
uint _marketIndex
) public view returns (ISportsAMMV2.CombinedPosition[] memory combinedPositions) {
return markets[_marketIndex].combinedPositions;
}
/// @notice return the payout for this ticket
/// @return systemBetPayout the payout for this ticket
function getSystemBetPayout() external view returns (uint systemBetPayout) {
systemBetPayout = _getSystemBetPayout();
}
/* ========== EXTERNAL WRITE FUNCTIONS ========== */
/// @notice exercise ticket
function exercise(address _exerciseCollateral) external onlyAMM notPaused returns (uint) {
bool isExercisable = isTicketExercisable();
require(isExercisable, "Ticket not exercisable yet");
require(expectedFinalPayout > 0, "Expected final payout not set");
uint payoutWithFees = expectedFinalPayout;
uint payout = payoutWithFees - fees;
bool isCancelled = false;
if (_isUserTheWinner()) {
finalPayout = payout;
isCancelled = true;
for (uint i = 0; i < numOfMarkets; i++) {
bool isCancelledMarketPosition = sportsAMM.resultManager().isCancelledMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
if (isCancelledMarketPosition) {
if (isSGP) {
isCancelled = true;
break;
}
finalPayout = (finalPayout * markets[i].odd) / ONE;
} else {
isCancelled = false;
}
}
finalPayout = isCancelled ? buyInAmount : (isSystem ? _getSystemBetPayout() : finalPayout);
collateral.safeTransfer(
_exerciseCollateral == address(0) || _exerciseCollateral == address(collateral)
? address(ticketOwner)
: address(sportsAMM),
finalPayout
);
}
// if user is lost or if the user payout was less than anticipated due to cancelled games, send the remainder to AMM
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(address(sportsAMM), balance);
}
_resolve(!isTicketLost(), isCancelled);
return finalPayout;
}
/// @notice expire ticket
function expire(address _beneficiary) external onlyAMM {
require(phase() == Phase.Expiry, "Ticket not in expiry phase");
require(!resolved, "Can't expire resolved ticket");
emit Expired(_beneficiary);
_selfDestruct(_beneficiary);
}
/// @notice cancel the ticket
function cancel() external onlyAMM notPaused returns (uint) {
finalPayout = buyInAmount;
collateral.safeTransfer(address(ticketOwner), finalPayout);
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(address(sportsAMM), balance);
}
_resolve(true, true);
return finalPayout;
}
/// @notice mark the ticket as lost
function markAsLost() external onlyAMM notPaused returns (uint) {
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(address(sportsAMM), balance);
}
_resolve(false, false);
isMarkedAsLost = true;
return 0;
}
/// @notice withdraw collateral from the ticket
function withdrawCollateral(address recipient) external onlyAMM {
collateral.safeTransfer(recipient, collateral.balanceOf(address(this)));
}
/* ========== INTERNAL FUNCTIONS ========== */
function _resolve(bool _hasUserWon, bool _cancelled) internal {
resolved = true;
cancelled = _cancelled;
emit Resolved(_hasUserWon, _cancelled);
}
function _selfDestruct(address beneficiary) internal {
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(beneficiary, balance);
}
}
function _isUserTheWinner() internal view returns (bool hasUserWon) {
if (areAllMarketsResolved()) {
hasUserWon = !isTicketLost();
}
}
/* ========== SETTERS ========== */
function setPaused(bool _paused) external {
require(msg.sender == address(sportsAMM.manager()), "Invalid sender");
if (paused == _paused) return;
paused = _paused;
emit PauseUpdated(_paused);
}
/* ========== SYSTEM BET UTILS ========== */
function _getSystemBetPayout() internal view returns (uint systemBetPayout) {
if (isSystem) {
uint8[][] memory systemCombinations = sportsAMM.riskManager().generateCombinations(
uint8(numOfMarkets),
systemBetDenominator
);
uint totalCombinations = systemCombinations.length;
uint buyinPerCombination = ((buyInAmount * ONE) / totalCombinations) / ONE;
bool[] memory winningMarkets = new bool[](numOfMarkets);
bool[] memory cancelledMarkets = new bool[](numOfMarkets);
for (uint i = 0; i < numOfMarkets; i++) {
if (
!sportsAMM.resultManager().isMarketResolved(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].combinedPositions
)
) {
return 0;
}
winningMarkets[i] = sportsAMM.resultManager().isWinningMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
cancelledMarkets[i] = sportsAMM.resultManager().isCancelledMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
}
// Loop through each stored combination
for (uint i = 0; i < totalCombinations; i++) {
uint8[] memory currentCombination = systemCombinations[i];
uint combinationQuote = ONE;
for (uint j = 0; j < currentCombination.length; j++) {
uint8 marketIndex = currentCombination[j];
if (winningMarkets[marketIndex]) {
if (!cancelledMarkets[marketIndex]) {
combinationQuote = (combinationQuote * markets[marketIndex].odd) / ONE;
}
} else {
combinationQuote = 0;
break;
}
}
if (combinationQuote > 0) {
uint combinationPayout = (buyinPerCombination * ONE) / combinationQuote;
systemBetPayout += combinationPayout;
}
}
uint maxPayout = (buyInAmount * ONE) / totalQuote;
if (systemBetPayout > maxPayout) {
systemBetPayout = maxPayout;
}
}
}
/* ========== MODIFIERS ========== */
modifier onlyAMM() {
require(msg.sender == address(sportsAMM), "Only the AMM may perform these methods");
_;
}
modifier notPaused() {
require(!paused, "Market paused");
_;
}
/* ========== EVENTS ========== */
event Resolved(bool isUserTheWinner, bool cancelled);
event Expired(address beneficiary);
event PauseUpdated(bool paused);
event ExpectedFinalPayoutSet(uint amount);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IProxyBetting.sol";
interface IFreeBetsHolder is IProxyBetting {
function confirmLiveTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount, address _collateral) external;
function confirmSGPTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount, address _collateral) external;
function balancePerUserAndCollateral(address user, address collateral) external view returns (uint);
function freeBetExpiration(address user, address collateral) external view returns (uint);
function freeBetExpirationUpgrade() external view returns (uint);
function freeBetExpirationPeriod() external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IProxyBetting {
function getActiveTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);
function numOfActiveTicketsPerUser(address _user) external view returns (uint);
function getResolvedTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);
function numOfResolvedTicketsPerUser(address _user) external view returns (uint);
function confirmTicketResolved(address _resolvedTicket) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/ISportsAMMV2Manager.sol";
import "../interfaces/ISportsAMMV2ResultManager.sol";
import "../interfaces/ISportsAMMV2RiskManager.sol";
import "../interfaces/ISportsAMMV2Manager.sol";
import "../interfaces/IFreeBetsHolder.sol";
import "../interfaces/IStakingThalesBettingProxy.sol";
interface ISportsAMMV2 {
enum TicketAction {
Exercise,
Cancel,
MarkLost
}
struct CombinedPosition {
uint16 typeId;
uint8 position;
int24 line;
}
struct TradeData {
bytes32 gameId;
uint16 sportId;
uint16 typeId;
uint maturity;
uint8 status;
int24 line;
uint24 playerId;
uint[] odds;
bytes32[] merkleProof;
uint8 position;
CombinedPosition[][] combinedPositions;
}
function defaultCollateral() external view returns (IERC20);
function manager() external view returns (ISportsAMMV2Manager);
function resultManager() external view returns (ISportsAMMV2ResultManager);
function safeBoxFee() external view returns (uint);
function handleTicketResolving(address _ticket, ISportsAMMV2.TicketAction action) external;
function riskManager() external view returns (ISportsAMMV2RiskManager);
function freeBetsHolder() external view returns (IFreeBetsHolder);
function stakingThalesBettingProxy() external view returns (IStakingThalesBettingProxy);
function tradeLive(
TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
address _recipient,
address _referrer,
address _collateral
) external returns (address _createdTicket);
function trade(
TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral,
bool _isEth
) external returns (address _createdTicket);
function tradeSystemBet(
TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral,
bool _isEth,
uint8 _systemBetDenominator
) external returns (address _createdTicket);
function tradeSGP(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _approvedQuote,
address _recipient,
address _referrer,
address _collateral
) external returns (address _createdTicket);
function rootPerGame(bytes32 game) external view returns (bytes32);
function getRootsPerGames(bytes32[] calldata _games) external view returns (bytes32[] memory _roots);
function paused() external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface ISportsAMMV2LiquidityPool {
function commitTrade(address ticket, uint amount) external;
function transferToPool(address ticket, uint amount) external;
function getTicketPool(address _ticket) external returns (address);
function collateralKey() external view returns (bytes32);
function getCollateralPrice() external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./ISportsAMMV2.sol";
interface ISportsAMMV2Manager {
enum Role {
ROOT_SETTING,
RISK_MANAGING,
MARKET_RESOLVING,
TICKET_PAUSER
}
function isWhitelistedAddress(address _address, Role role) external view returns (bool);
function decimals() external view returns (uint);
function feeToken() external view returns (address);
function isActiveTicket(address _ticket) external view returns (bool);
function getActiveTickets(uint _index, uint _pageSize) external view returns (address[] memory);
function numOfActiveTickets() external view returns (uint);
function getActiveTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);
function numOfActiveTicketsPerUser(address _user) external view returns (uint);
function getResolvedTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);
function numOfResolvedTicketsPerUser(address _user) external view returns (uint);
function getTicketsPerGame(uint _index, uint _pageSize, bytes32 _gameId) external view returns (address[] memory);
function numOfTicketsPerGame(bytes32 _gameId) external view returns (uint);
function isKnownTicket(address _ticket) external view returns (bool);
function sportsAMM() external view returns (address);
function getTicketsPerMarket(
uint _index,
uint _pageSize,
bytes32 _gameId,
uint _typeId,
uint _playerId
) external view returns (address[] memory);
function numOfTicketsPerMarket(bytes32 _gameId, uint _typeId, uint _playerId) external view returns (uint);
function addNewKnownTicket(ISportsAMMV2.TradeData[] memory _tradeData, address ticket, address user) external;
function resolveKnownTicket(address ticket, address ticketOwner) external;
function expireKnownTicket(address ticket, address ticketOwner) external;
function isSystemTicket(address _ticket) external view returns (bool);
function isSGPTicket(address _ticket) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ISportsAMMV2.sol";
interface ISportsAMMV2ResultManager {
enum MarketPositionStatus {
Open,
Cancelled,
Winning,
Losing
}
function isMarketResolved(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
ISportsAMMV2.CombinedPosition[] memory combinedPositions
) external view returns (bool isResolved);
function getMarketPositionStatus(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (MarketPositionStatus status);
function isWinningMarketPosition(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (bool isWinning);
function isCancelledMarketPosition(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (bool isCancelled);
function getResultsPerMarket(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId
) external view returns (int24[] memory results);
function resultTypePerMarketType(uint _typeId) external view returns (uint8 marketType);
function isMarketResolvedAndPositionWinning(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (bool isResolved, bool isWinning);
function setResultsPerMarkets(
bytes32[] memory _gameIds,
uint16[] memory _typeIds,
uint24[] memory _playerIds,
int24[][] memory _results
) external;
function isGameCancelled(bytes32 _gameId) external view returns (bool);
function cancelGames(bytes32[] memory _gameIds) external;
function cancelMarkets(
bytes32[] memory _gameIds,
uint16[] memory _typeIds,
uint24[] memory _playerIds,
int24[] memory _lines
) external;
function cancelMarket(bytes32 _gameId, uint16 _typeId, uint24 _playerId, int24 _line) external;
function cancelGame(bytes32 _gameId) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./ISportsAMMV2.sol";
interface ISportsAMMV2RiskManager {
struct TypeCap {
uint typeId;
uint cap;
}
struct CapData {
uint capPerSport;
uint capPerChild;
TypeCap[] capPerType;
}
struct DynamicLiquidityData {
uint cutoffTimePerSport;
uint cutoffDividerPerSport;
}
struct RiskData {
uint sportId;
CapData capData;
uint riskMultiplierPerSport;
DynamicLiquidityData dynamicLiquidityData;
}
enum RiskStatus {
NoRisk,
OutOfLiquidity,
InvalidCombination
}
function minBuyInAmount() external view returns (uint);
function maxTicketSize() external view returns (uint);
function maxSupportedAmount() external view returns (uint);
function maxSupportedOdds() external view returns (uint);
function maxAllowedSystemCombinations() external view returns (uint);
function expiryDuration() external view returns (uint);
function liveTradingPerSportAndTypeEnabled(uint _sportId, uint _typeId) external view returns (bool _enabled);
function calculateCapToBeUsed(
bytes32 _gameId,
uint16 _sportId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _maturity,
bool _isLive
) external view returns (uint cap);
function calculateTotalRiskOnGame(
bytes32 _gameId,
uint16 _sportId,
uint _maturity
) external view returns (uint totalRisk);
function checkRisks(
ISportsAMMV2.TradeData[] memory _tradeData,
uint _buyInAmount,
bool _isLive,
uint8 _systemBetDenominator
) external view returns (ISportsAMMV2RiskManager.RiskStatus riskStatus, bool[] memory isMarketOutOfLiquidity);
function checkLimits(
uint _buyInAmount,
uint _totalQuote,
uint _payout,
uint _expectedPayout,
uint _additionalSlippage,
uint _ticketSize
) external view;
function spentOnGame(bytes32 _gameId) external view returns (uint);
function riskPerMarketTypeAndPosition(
bytes32 _gameId,
uint _typeId,
uint _playerId,
uint _position
) external view returns (int);
function checkAndUpdateRisks(
ISportsAMMV2.TradeData[] memory _tradeData,
uint _buyInAmount,
uint _payout,
bool _isLive,
uint8 _systemBetDenominator,
bool _isSGP
) external;
function verifyMerkleTree(ISportsAMMV2.TradeData memory _marketTradeData, bytes32 _rootPerGame) external pure;
function batchVerifyMerkleTree(
ISportsAMMV2.TradeData[] memory _marketTradeData,
bytes32[] memory _rootPerGame
) external pure;
function isSportIdFuture(uint16 _sportsId) external view returns (bool);
function sgpOnSportIdEnabled(uint16 _sportsId) external view returns (bool);
function getMaxSystemBetPayout(
ISportsAMMV2.TradeData[] memory _tradeData,
uint8 _systemBetDenominator,
uint _buyInAmount,
uint _addedPayoutPercentage
) external view returns (uint systemBetPayout, uint systemBetQuote);
function generateCombinations(uint8 n, uint8 k) external pure returns (uint8[][] memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IProxyBetting.sol";
interface IStakingThalesBettingProxy is IProxyBetting {
function preConfirmLiveTrade(bytes32 requestId, uint _buyInAmount) external;
function confirmLiveTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount) external;
function preConfirmSGPTrade(bytes32 requestId, uint _buyInAmount) external;
function confirmSGPTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IWeth {
function deposit() external payable;
function withdraw(uint256) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// 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.20;
// Inheritance
import "./ProxyOwned.sol";
// Clone of syntetix contract without constructor
contract ProxyPausable is ProxyOwned {
uint public lastPauseTime;
bool public paused;
/**
* @notice Change the paused state of the contract
* @dev Only the contract owner may call this.
*/
function setPaused(bool _paused) external onlyOwner {
// Ensure we're actually changing the state before we do anything
if (_paused == paused) {
return;
}
// Set our paused state.
paused = _paused;
// If applicable, set the last pause time.
if (paused) {
lastPauseTime = block.timestamp;
}
// Let everyone know that our pause state has changed.
emit PauseChanged(paused);
}
event PauseChanged(bool isPaused);
modifier notPaused() {
require(!paused, "This action cannot be performed while the contract is paused");
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier
* available, which can be aplied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*/
contract ProxyReentrancyGuard {
/// @dev counter to allow mutex lock with only one SSTORE operation
uint256 private _guardCounter;
bool private _initialized;
function initNonReentrant() public {
require(!_initialized, "Already initialized");
_initialized = true;
_guardCounter = 1;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_guardCounter += 1;
uint256 localCounter = _guardCounter;
_;
require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
}
}{
"optimizer": {
"enabled": true,
"runs": 100
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ERC1167FailedCreateClone","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"IllegalInputAmounts","type":"error"},{"inputs":[],"name":"InsuffETHSent","type":"error"},{"inputs":[],"name":"InsuffReceived","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidLength","type":"error"},{"inputs":[],"name":"InvalidPosition","type":"error"},{"inputs":[],"name":"InvalidSender","type":"error"},{"inputs":[],"name":"MultiCollatDisabled","type":"error"},{"inputs":[],"name":"MultiCollateralDisabled","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OfframpOnlyDefaultCollateralAllowed","type":"error"},{"inputs":[],"name":"OnlyDedicatedProcessor","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"OnlyTicketOwner","type":"error"},{"inputs":[],"name":"SBDOutOfRange","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"UndefinedRecipient","type":"error"},{"inputs":[],"name":"UnknownTicket","type":"error"},{"inputs":[],"name":"UnsupportedSender","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"inputs":[],"name":"ZeroPriceForCollateral","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"defaultCollateral","type":"address"},{"indexed":false,"internalType":"address","name":"manager","type":"address"},{"indexed":false,"internalType":"address","name":"riskManager","type":"address"},{"indexed":false,"internalType":"address","name":"resultManager","type":"address"},{"indexed":false,"internalType":"address","name":"referrals","type":"address"},{"indexed":false,"internalType":"address","name":"safeBox","type":"address"}],"name":"AddressesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"safeBoxFee","type":"uint256"}],"name":"AmountsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"address","name":"liquidityPool","type":"address"},{"indexed":false,"internalType":"uint256","name":"addedPayout","type":"uint256"},{"indexed":false,"internalType":"address","name":"safeBox","type":"address"}],"name":"CollateralConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"game","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"GameRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bytes32","name":"gameId","type":"bytes32"},{"internalType":"uint16","name":"sportId","type":"uint16"},{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"},{"internalType":"uint24","name":"playerId","type":"uint24"},{"internalType":"uint8","name":"position","type":"uint8"},{"internalType":"uint256","name":"odd","type":"uint256"},{"components":[{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint8","name":"position","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"}],"internalType":"struct ISportsAMMV2.CombinedPosition[]","name":"combinedPositions","type":"tuple[]"}],"indexed":false,"internalType":"struct Ticket.MarketData[]","name":"markets","type":"tuple[]"},{"indexed":false,"internalType":"address","name":"ticket","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payout","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isLive","type":"bool"}],"name":"NewTicket","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"refferer","type":"address"},{"indexed":false,"internalType":"address","name":"trader","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"volume","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"name":"ReferrerPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"safeBoxFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"safeBoxAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"name":"SafeBoxFeePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liveTradingProcessor","type":"address"},{"indexed":false,"internalType":"address","name":"sgpTradingProcessor","type":"address"},{"indexed":false,"internalType":"address","name":"freeBetsHolder","type":"address"}],"name":"SetBettingProcessors","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"onOffRamper","type":"address"}],"name":"SetMultiCollateralOnOffRamp","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"ticket","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"payout","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalQuote","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"name":"TicketCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"ticketMastercopy","type":"address"}],"name":"TicketMastercopyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"ticket","type":"address"},{"indexed":false,"internalType":"address","name":"ticketOwner","type":"address"},{"indexed":false,"internalType":"bool","name":"isUserTheWinner","type":"bool"}],"name":"TicketResolved","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addedPayoutPercentagePerCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"address","name":"_liquidityPool","type":"address"},{"internalType":"uint256","name":"_addedPayout","type":"uint256"},{"internalType":"address","name":"_safeBox","type":"address"}],"name":"configureCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultCollateral","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_ticket","type":"address"},{"internalType":"address","name":"_exerciseCollateral","type":"address"}],"name":"exerciseTicketOffRamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tickets","type":"address[]"}],"name":"expireTickets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freeBetsHolder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_games","type":"bytes32[]"}],"name":"getRootsPerGames","outputs":[{"internalType":"bytes32[]","name":"_roots","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_ticket","type":"address"},{"internalType":"enum ISportsAMMV2.TicketAction","name":"action","type":"uint8"}],"name":"handleTicketResolving","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initNonReentrant","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastPauseTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"liquidityPoolForCollateral","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liveTradingProcessor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"contract ISportsAMMV2Manager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multiCollateralOnOffRamp","outputs":[{"internalType":"contract IMultiCollateralOnOffRamp","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referrals","outputs":[{"internalType":"contract IReferrals","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"resultManager","outputs":[{"internalType":"contract ISportsAMMV2ResultManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"riskManager","outputs":[{"internalType":"contract ISportsAMMV2RiskManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"rootPerGame","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"safeBox","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"safeBoxFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"safeBoxPerCollateral","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_defaultCollateral","type":"address"},{"internalType":"address","name":"_manager","type":"address"},{"internalType":"address","name":"_riskManager","type":"address"},{"internalType":"address","name":"_resultManager","type":"address"},{"internalType":"address","name":"_referrals","type":"address"},{"internalType":"address","name":"_safeBox","type":"address"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_safeBoxFee","type":"uint256"}],"name":"setAmounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_liveTradingProcessor","type":"address"},{"internalType":"address","name":"_sgpTradingProcessor","type":"address"},{"internalType":"address","name":"_freeBetsHolder","type":"address"}],"name":"setBettingProcessors","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_onOffRamper","type":"address"}],"name":"setMultiCollateralOnOffRamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_game","type":"bytes32"},{"internalType":"bytes32","name":"_root","type":"bytes32"}],"name":"setRootForGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_games","type":"bytes32[]"},{"internalType":"bytes32[]","name":"_roots","type":"bytes32[]"}],"name":"setRootsPerGames","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_ticketMastercopy","type":"address"}],"name":"setTicketMastercopy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sgpTradingProcessor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingThalesBettingProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ticketMastercopy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"gameId","type":"bytes32"},{"internalType":"uint16","name":"sportId","type":"uint16"},{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"},{"internalType":"uint24","name":"playerId","type":"uint24"},{"internalType":"uint256[]","name":"odds","type":"uint256[]"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint8","name":"position","type":"uint8"},{"components":[{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint8","name":"position","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"}],"internalType":"struct ISportsAMMV2.CombinedPosition[][]","name":"combinedPositions","type":"tuple[][]"}],"internalType":"struct ISportsAMMV2.TradeData[]","name":"_tradeData","type":"tuple[]"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"uint256","name":"_expectedQuote","type":"uint256"},{"internalType":"uint256","name":"_additionalSlippage","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"bool","name":"_isEth","type":"bool"}],"name":"trade","outputs":[{"internalType":"address","name":"_createdTicket","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"gameId","type":"bytes32"},{"internalType":"uint16","name":"sportId","type":"uint16"},{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"},{"internalType":"uint24","name":"playerId","type":"uint24"},{"internalType":"uint256[]","name":"odds","type":"uint256[]"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint8","name":"position","type":"uint8"},{"components":[{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint8","name":"position","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"}],"internalType":"struct ISportsAMMV2.CombinedPosition[][]","name":"combinedPositions","type":"tuple[][]"}],"internalType":"struct ISportsAMMV2.TradeData[]","name":"_tradeData","type":"tuple[]"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"uint256","name":"_expectedQuote","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_collateral","type":"address"}],"name":"tradeLive","outputs":[{"internalType":"address","name":"_createdTicket","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"gameId","type":"bytes32"},{"internalType":"uint16","name":"sportId","type":"uint16"},{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"},{"internalType":"uint24","name":"playerId","type":"uint24"},{"internalType":"uint256[]","name":"odds","type":"uint256[]"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint8","name":"position","type":"uint8"},{"components":[{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint8","name":"position","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"}],"internalType":"struct ISportsAMMV2.CombinedPosition[][]","name":"combinedPositions","type":"tuple[][]"}],"internalType":"struct ISportsAMMV2.TradeData[]","name":"_tradeData","type":"tuple[]"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"bool","name":"_isLive","type":"bool"}],"name":"tradeQuote","outputs":[{"internalType":"uint256","name":"totalQuote","type":"uint256"},{"internalType":"uint256","name":"payout","type":"uint256"},{"internalType":"uint256","name":"fees","type":"uint256"},{"internalType":"uint256[]","name":"amountsToBuy","type":"uint256[]"},{"internalType":"uint256","name":"buyInAmountInDefaultCollateral","type":"uint256"},{"internalType":"enum ISportsAMMV2RiskManager.RiskStatus","name":"riskStatus","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"gameId","type":"bytes32"},{"internalType":"uint16","name":"sportId","type":"uint16"},{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"},{"internalType":"uint24","name":"playerId","type":"uint24"},{"internalType":"uint256[]","name":"odds","type":"uint256[]"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint8","name":"position","type":"uint8"},{"components":[{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint8","name":"position","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"}],"internalType":"struct ISportsAMMV2.CombinedPosition[][]","name":"combinedPositions","type":"tuple[][]"}],"internalType":"struct ISportsAMMV2.TradeData[]","name":"_tradeData","type":"tuple[]"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"bool","name":"_isLive","type":"bool"},{"internalType":"uint8","name":"_systemBetDenominator","type":"uint8"}],"name":"tradeQuoteSystem","outputs":[{"internalType":"uint256","name":"totalQuote","type":"uint256"},{"internalType":"uint256","name":"payout","type":"uint256"},{"internalType":"uint256","name":"fees","type":"uint256"},{"internalType":"uint256[]","name":"amountsToBuy","type":"uint256[]"},{"internalType":"uint256","name":"buyInAmountInDefaultCollateral","type":"uint256"},{"internalType":"enum ISportsAMMV2RiskManager.RiskStatus","name":"riskStatus","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"gameId","type":"bytes32"},{"internalType":"uint16","name":"sportId","type":"uint16"},{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"},{"internalType":"uint24","name":"playerId","type":"uint24"},{"internalType":"uint256[]","name":"odds","type":"uint256[]"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint8","name":"position","type":"uint8"},{"components":[{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint8","name":"position","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"}],"internalType":"struct ISportsAMMV2.CombinedPosition[][]","name":"combinedPositions","type":"tuple[][]"}],"internalType":"struct ISportsAMMV2.TradeData[]","name":"_tradeData","type":"tuple[]"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"uint256","name":"_approvedQuote","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_collateral","type":"address"}],"name":"tradeSGP","outputs":[{"internalType":"address","name":"_createdTicket","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"gameId","type":"bytes32"},{"internalType":"uint16","name":"sportId","type":"uint16"},{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint256","name":"maturity","type":"uint256"},{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"},{"internalType":"uint24","name":"playerId","type":"uint24"},{"internalType":"uint256[]","name":"odds","type":"uint256[]"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"},{"internalType":"uint8","name":"position","type":"uint8"},{"components":[{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"uint8","name":"position","type":"uint8"},{"internalType":"int24","name":"line","type":"int24"}],"internalType":"struct ISportsAMMV2.CombinedPosition[][]","name":"combinedPositions","type":"tuple[][]"}],"internalType":"struct ISportsAMMV2.TradeData[]","name":"_tradeData","type":"tuple[]"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"uint256","name":"_expectedQuote","type":"uint256"},{"internalType":"uint256","name":"_additionalSlippage","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"bool","name":"_isEth","type":"bool"},{"internalType":"uint8","name":"_systemBetDenominator","type":"uint8"}],"name":"tradeSystemBet","outputs":[{"internalType":"address","name":"_createdTicket","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAddress","type":"address"}],"name":"transferOwnershipAtInit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ticketAddress","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawCollateralFromTicket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
608060405234801561001057600080fd5b5061551e806100206000396000f3fe60806040526004361061028c5760003560e01c806391b4ded91161015a578063c3b83f5f116100c1578063e6d1c8a61161007a578063e6d1c8a6146107ed578063ebc797721461080d578063f453ea7d14610822578063f5bf372e14610842578063f8a940b01461086f578063fd47e5ec1461088f57600080fd5b8063c3b83f5f1461072d578063c4d66de81461074d578063ce18a8371461076d578063d3dc75391461078d578063d43b3576146107ad578063e481a342146107cd57600080fd5b8063aeb0f16411610113578063aeb0f16414610657578063b4e7e5c614610677578063b6a3670414610697578063b8e15ed0146106b7578063c2c9e276146106ed578063c301086c1461070d57600080fd5b806391b4ded9146105ae57806393b0c2ae146105c4578063942b67dc146105d75780639a23be78146105f75780639a618c0f14610617578063ace81b711461063757600080fd5b806347842663116101fe5780636cfb6bf9116101b75780636cfb6bf9146104e75780636dbf6cc7146105075780637649226c1461052757806379ba5097146105595780638da5cb5b1461056e5780638ff9d5cf1461058e57600080fd5b80634784266314610410578063481c6a751461043057806348663e951461045057806350631ddc1461047057806353a47bb71461049d5780635c975abb146104bd57600080fd5b80631627540c116102505780631627540c1461034357806316c38b3c14610363578063177957a81461038357806329cf5368146103b957806338ea7103146103cc57806344cab7ee146103f057600080fd5b8063014e17b01461029857806304de96c5146102c15780630ca08207146102e15780630fe621901461030357806313af40351461032357600080fd5b3661029357005b600080fd5b6102ab6102a63660046141ec565b6108bc565b6040516102b89190614284565b60405180910390f35b3480156102cd57600080fd5b506013546102ab906001600160a01b031681565b3480156102ed57600080fd5b506103016102fc366004614298565b61096b565b005b34801561030f57600080fd5b506017546102ab906001600160a01b031681565b34801561032f57600080fd5b5061030161033e366004614298565b6109e6565b34801561034f57600080fd5b5061030161035e366004614298565b610af2565b34801561036f57600080fd5b5061030161037e3660046142b5565b610b45565b34801561038f57600080fd5b506102ab61039e366004614298565b6008602052600090815260409020546001600160a01b031681565b6102ab6103c73660046142d2565b610bb7565b3480156103d857600080fd5b506103e260105481565b6040519081526020016102b8565b3480156103fc57600080fd5b506018546102ab906001600160a01b031681565b34801561041c57600080fd5b50600b546102ab906001600160a01b031681565b34801561043c57600080fd5b50600a546102ab906001600160a01b031681565b34801561045c57600080fd5b50600f546102ab906001600160a01b031681565b34801561047c57600080fd5b506103e261048b366004614361565b60066020526000908152604090205481565b3480156104a957600080fd5b506001546102ab906001600160a01b031681565b3480156104c957600080fd5b506003546104d79060ff1681565b60405190151581526020016102b8565b3480156104f357600080fd5b5061030161050236600461437a565b610cb1565b34801561051357600080fd5b5061030161052236600461451b565b610df3565b34801561053357600080fd5b5061054761054236600461457e565b610f14565b6040516102b896959493929190614607565b34801561056557600080fd5b50610301610f52565b34801561057a57600080fd5b506000546102ab906001600160a01b031681565b34801561059a57600080fd5b50600c546102ab906001600160a01b031681565b3480156105ba57600080fd5b506103e260025481565b6102ab6105d2366004614694565b61102a565b3480156105e357600080fd5b506103016105f236600461473b565b611108565b34801561060357600080fd5b5061030161061236600461475d565b6111c0565b34801561062357600080fd5b506011546102ab906001600160a01b031681565b34801561064357600080fd5b50610547610652366004614796565b61122a565b34801561066357600080fd5b506007546102ab906001600160a01b031681565b34801561068357600080fd5b50610301610692366004614816565b611269565b3480156106a357600080fd5b50600e546102ab906001600160a01b031681565b3480156106c357600080fd5b506102ab6106d2366004614298565b6016602052600090815260409020546001600160a01b031681565b3480156106f957600080fd5b50610301610708366004614861565b6112f5565b34801561071957600080fd5b50610301610728366004614298565b611497565b34801561073957600080fd5b50610301610748366004614298565b6114ea565b34801561075957600080fd5b50610301610768366004614298565b6115da565b34801561077957600080fd5b506014546102ab906001600160a01b031681565b34801561079957600080fd5b50600d546102ab906001600160a01b031681565b3480156107b957600080fd5b506103016107c83660046148a2565b6116e4565b3480156107d957600080fd5b506102ab6107e83660046142d2565b6117ee565b3480156107f957600080fd5b5061030161080836600461475d565b6118c1565b34801561081957600080fd5b50610301611a4e565b34801561082e57600080fd5b5061030161083d366004614361565b611aac565b34801561084e57600080fd5b506103e261085d366004614298565b60156020526000908152604090205481565b34801561087b57600080fd5b5061030161088a366004614902565b611ae9565b34801561089b57600080fd5b506108af6108aa366004614861565b611cc1565b6040516102b8919061496b565b60006001600460008282546108d19190614994565b909155505060045460035460ff16156109055760405162461bcd60e51b81526004016108fc906149a7565b60405180910390fd5b61093b6109128a8c614b51565b60408051606081018252600080825260208201819052918101919091528a8a8a8a8a8a33611d6c565b9150600454811461095e5760405162461bcd60e51b81526004016108fc90614cc2565b5098975050505050505050565b610973611e4b565b600754601154610990916001600160a01b03908116911683611ebf565b601180546001600160a01b0319166001600160a01b0383161790556040517f7d16d7e66eb2263f48c2c24af20e095395796ce3d05df64d84d9991a4d436247906109db908390614284565b60405180910390a150565b6001600160a01b038116610a385760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064016108fc565b600154600160a01b900460ff1615610aa45760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b60648201526084016108fc565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b03199091161781556040516000805160206154c9833981519152916109db918490614cf9565b610afa611e4b565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906109db908390614284565b610b4d611e4b565b60035460ff16151581151514610bb4576003805460ff191682151590811790915560ff1615610b7b57426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec5906020016109db565b50565b6000600160046000828254610bcc9190614994565b909155505060045460035460ff1615610bf75760405162461bcd60e51b81526004016108fc906149a7565b846001600160a01b038116610c1f576040516328f6040160e21b815260040160405180910390fd5b6018546001600160a01b03163314610c4a57604051637bcec4fb60e01b815260040160405180910390fd5b610c81610c578a8c614b51565b60408051606081018252600080825260016020830152918101829052908b908b908a8a828e611d6c565b9250506004548114610ca55760405162461bcd60e51b81526004016108fc90614cc2565b50979650505050505050565b610cb9611e4b565b600780546001600160a01b0319166001600160a01b0388169081179091556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa158015610d12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d369190614d13565b600955600a80546001600160a01b038781166001600160a01b03199283168117909355600b80548883169084168117909155600c80548884169085168117909155600d80548885169086168117909155600f805488861696168617905560408051948d1685526020850196909652948301919091526060820152608081019290925260a08201527f1962de5c532b26a8a72c8524fe745d05421f16a6521fc531ce010572f742a6939060c0015b60405180910390a1505050505050565b600a5460405163e760c39560e01b81526001600160a01b039091169063e760c39590610e26903390600090600401614d2c565b602060405180830381865afa158015610e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e679190614d59565b158015610e7f57506000546001600160a01b03163314155b15610e9d57604051636edaef2f60e11b815260040160405180910390fd5b8051825114610ebf5760405163251f56a160e21b815260040160405180910390fd5b60005b8251811015610f0f57610f07838281518110610ee057610ee0614d76565b6020026020010151838381518110610efa57610efa614d76565b6020026020010151611fca565b600101610ec2565b505050565b60008060006060600080610f2d8b8b8b8b8b6000612019565b809650819750829850839950849a50859b505050505050509550955095509550955095565b6001546001600160a01b03163314610fca5760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b60648201526084016108fc565b6000546001546040516000805160206154c983398151915292610ffb926001600160a01b0391821692911690614cf9565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b600060016004600082825461103f9190614994565b909155505060045460035460ff161561106a5760405162461bcd60e51b81526004016108fc906149a7565b60018360ff16111580611080575060ff83168a11155b1561109e57604051631a0a722b60e01b815260040160405180910390fd5b6110d76110ab8b8d614b51565b6040805160608101825260ff87168152600060208201819052918101919091528b8b8b8b8b8b33611d6c565b915060045481146110fa5760405162461bcd60e51b81526004016108fc90614cc2565b509998505050505050505050565b600a5460405163e760c39560e01b81526001600160a01b039091169063e760c3959061113b903390600090600401614d2c565b602060405180830381865afa158015611158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117c9190614d59565b15801561119457506000546001600160a01b03163314155b156111b257604051636edaef2f60e11b815260040160405180910390fd5b6111bc8282611fca565b5050565b6111c8611e4b565b604051634946980760e11b81526001600160a01b0383169063928d300e906111f4908490600401614284565b600060405180830381600087803b15801561120e57600080fd5b505af1158015611222573d6000803e3d6000fd5b505050505050565b600080600060606000806112428c8c8c8c8c8c612019565b809650819750829850839950849a50859b5050505050505096509650965096509650969050565b611271611e4b565b601380546001600160a01b038581166001600160a01b0319928316811790935560188054868316908416811790915560148054928616929093168217909255604080519384526020840192909252908201527f2b8bc92a77df3bf6abc793bbc13c813f4e0028dd6de7aefb204a96ade4ddc4739060600160405180910390a1505050565b6112fd611e4b565b60005b81811015610f0f57600083838381811061131c5761131c614d76565b90506020020160208101906113319190614298565b90506000816001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611372573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113969190614d8c565b6014549091506001600160a01b038083169116148015906113c557506017546001600160a01b03828116911614155b1561148d5760405163646d919f60e11b81526001600160a01b0383169063c8db233e906113f6903390600401614284565b600060405180830381600087803b15801561141057600080fd5b505af1158015611424573d6000803e3d6000fd5b5050600a5460405163f033d0f960e01b81526001600160a01b03909116925063f033d0f9915061145a9085908590600401614cf9565b600060405180830381600087803b15801561147457600080fd5b505af1158015611488573d6000803e3d6000fd5b505050505b5050600101611300565b61149f611e4b565b600e80546001600160a01b0319166001600160a01b0383161790556040517fab496e3942a33c5a729be5985af46209e18077a462af550b750246460950eb7f906109db908390614284565b6114f2611e4b565b6001600160a01b03811661153a5760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016108fc565b600154600160a81b900460ff161561158a5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b60448201526064016108fc565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b1790556040516000805160206154c9833981519152916109db918490614cf9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b031660008115801561161f5750825b90506000826001600160401b0316600114801561163b5750303b155b905081158015611649575080155b156116675760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561169157845460ff60401b1916600160401b1785555b61169a866109e6565b6116a2611a4e565b831561122257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610de3565b6116ec611e4b565b6001600160a01b03848116600090815260086020526040902054811690841614611766576001600160a01b038085166000908152600860205260409020546117379186911685611ebf565b6001600160a01b03848116600090815260086020526040902080546001600160a01b0319169185169190911790555b6001600160a01b038481166000818152601560209081526040808320879055601682529182902080546001600160a01b031916868616908117909155825193845293871690830152810184905260608101919091527f2083b2a6d9c12b8cd65a8a4d92a82708aac25a81a524e9d13e92559bf818db569060800160405180910390a150505050565b60006001600460008282546118039190614994565b909155505060045460035460ff161561182e5760405162461bcd60e51b81526004016108fc906149a7565b846001600160a01b038116611856576040516328f6040160e21b815260040160405180910390fd5b6013546001600160a01b0316331461188157604051637bcec4fb60e01b815260040160405180910390fd5b610c8161188e8a8c614b51565b6040518060600160405280600060ff168152602001600015158152602001600115158152508a8a60008a8a60008e611d6c565b6001600460008282546118d49190614994565b909155505060045460035460ff16156118ff5760405162461bcd60e51b81526004016108fc906149a7565b600a546040516321875b5760e01b815284916001600160a01b0316906321875b579061192f908490600401614284565b602060405180830381865afa15801561194c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119709190614d59565b61198d57604051634f8c20e160e01b815260040160405180910390fd5b836001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ee9190614d8c565b6001600160a01b0316336001600160a01b031614611a1f5760405163bba02e7160e01b815260040160405180910390fd5b611a2c8484600080612264565b506004548114610f0f5760405162461bcd60e51b81526004016108fc90614cc2565b60055460ff1615611a975760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016108fc565b6005805460ff19166001908117909155600455565b611ab4611e4b565b60108190556040518181527f5da87bc83066ddf563cfd9de25e562860f147b358751f50766847996202e5d33906020016109db565b600160046000828254611afc9190614994565b909155505060045460035460ff1615611b275760405162461bcd60e51b81526004016108fc906149a7565b600a546040516321875b5760e01b815284916001600160a01b0316906321875b5790611b57908490600401614284565b602060405180830381865afa158015611b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b989190614d59565b611bb557604051634f8c20e160e01b815260040160405180910390fd5b6000836002811115611bc957611bc96145f1565b14611c5f57600a5460405163e760c39560e01b81526001600160a01b039091169063e760c39590611c01903390600290600401614d2c565b602060405180830381865afa158015611c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c429190614d59565b611c5f57604051637830359760e01b815260040160405180910390fd5b6001836002811115611c7357611c736145f1565b03611c8c57611c8784600060016000612264565b611a2c565b6002836002811115611ca057611ca06145f1565b03611cb357611c87846000806001612264565b611a2c846000806000612264565b606081806001600160401b03811115611cdc57611cdc6143fc565b604051908082528060200260200182016040528015611d05578160200160208202803683370190505b50915060005b81811015611d645760066000868684818110611d2957611d29614d76565b90506020020135815260200190815260200160002054838281518110611d5157611d51614d76565b6020908102919091010152600101611d0b565b505092915050565b6000861580611d79575087155b15611d975760405163d1892e7760e01b815260040160405180910390fd5b611da185836128a7565b600080611db08a878688612904565b604080516101208101909152828152919d5098509193509150611e3b908d9060208101611ddd8e8e612d02565b81526020018b8152602001876001600160a01b031681526020018e6040015115158152602001896001600160a01b03168152602001856001600160a01b031681526020018481526020018e6020015115158152508d60000151612d2a565b9c9b505050505050505050505050565b6000546001600160a01b03163314611ebd5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b60648201526084016108fc565b565b6001600160a01b03821615611f425760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390611efd908590600090600401614da9565b6020604051808303816000875af1158015611f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f409190614d59565b505b6001600160a01b03811615610f0f5760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390611f8190849060001990600401614da9565b6020604051808303816000875af1158015611fa0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc49190614d59565b50505050565b60008281526006602090815260409182902083905581518481529081018390527fc6fa3d673d901ef180e5a314ff8ede38ac8ba226ce71c9d822ed8a438020a1ab910160405180910390a15050565b6000808060608782816001600160a01b038a161580159061204857506007546001600160a01b038b8116911614155b156121cd576001600160a01b03808b1660009081526008602052604090205416806120fb57601160009054906101000a90046001600160a01b03166001600160a01b031663cb4ba5248c8e6040518363ffffffff1660e01b81526004016120b0929190614da9565b602060405180830381865afa1580156120cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f19190614d13565b93508391506121cb565b6121c88c826001600160a01b031663f7683bbc6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561213d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121619190614d13565b8d6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561219f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c39190614d13565b613288565b93505b505b806000036121ee57604051631f2a200560e01b815260040160405180910390fd5b6122406121fb8d8f614b51565b6040805160e081018252848152600160208201529081018690526001600160a01b038d1660608201528b15156080820152600060a0820181905260c08201528a6132fe565b809650819850829950839a50849b5050505050505096509650965096509650969050565b83600082156122d857816001600160a01b0316636a265aa96040518163ffffffff1660e01b81526004016020604051808303816000875af11580156122ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d19190614d13565b9050612390565b831561231e57816001600160a01b031663ea8a1af06040518163ffffffff1660e01b81526004016020604051808303816000875af11580156122ad573d6000803e3d6000fd5b604051630d8acc1560e11b81526001600160a01b03831690631b15982a9061234a908890600401614284565b6020604051808303816000875af1158015612369573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238d9190614d13565b90505b6000826001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f49190614d8c565b90506000836001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612435573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124599190614d8c565b6014549091506001600160a01b038083169116148061248557506017546001600160a01b038281169116145b156124e95760405163509eb5b560e11b81526001600160a01b0382169063a13d6b6a906124b6908b90600401614284565b600060405180830381600087803b1580156124d057600080fd5b505af11580156124e4573d6000803e3d6000fd5b505050505b836001600160a01b0316639a82a09a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612527573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061254b9190614d59565b6125bd576125bb846001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa158015612590573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b49190614d13565b8284613860565b505b600a546040516306ef036760e01b81526001600160a01b03909116906306ef0367906125ef908b908590600401614cf9565b600060405180830381600087803b15801561260957600080fd5b505af115801561261d573d6000803e3d6000fd5b505050507f1a208f07d825103c5b1eb3bb1109b420fd544123300beb9e220fc951cbd7dd0b8882866001600160a01b0316633356a35a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612682573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126a69190614d59565b604080516001600160a01b03948516815293909216602084015215159082015260600160405180910390a16000831180156126e957506001600160a01b03871615155b80156127075750816001600160a01b0316876001600160a01b031614155b156127c6576007546001600160a01b0383811691161461273a5760405163c6338d5560e01b815260040160405180910390fd5b601154604051630992646d60e31b81526127c69183916001600160a01b0390911690634c93236890612772908c908990600401614da9565b6020604051808303816000875af1158015612791573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b59190614d13565b6001600160a01b038a169190613b44565b6001600160a01b03808316600081815260086020526040908190205490516370a0823160e01b815292169163d7efa129918b916370a082319061280d903090600401614284565b602060405180830381865afa15801561282a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284e9190614d13565b6040518363ffffffff1660e01b815260040161286b929190614da9565b600060405180830381600087803b15801561288557600080fd5b505af1158015612899573d6000803e3d6000fd5b505050505050505050505050565b6001600160a01b038216158015906128cd57506014546001600160a01b03828116911614155b156111bc57600d5460405163bbddaca360e01b81526001600160a01b039091169063bbddaca3906111f49085908590600401614cf9565b60008085816001600160a01b038716158061292c57506007546001600160a01b038881169116145b1561294f57506007546001600160a01b031661294a8187308b613b9c565b612cd9565b50858415612a6457601160009054906101000a90046001600160a01b03166001600160a01b0316634aa4a4fc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156129aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129ce9190614d8c565b6001600160a01b0316876001600160a01b03161415806129ed57508734105b15612a0b57604051634f4d1d8760e01b815260040160405180910390fd5b866001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015612a4657600080fd5b505af1158015612a5a573d6000803e3d6000fd5b5050505050612a79565b612a796001600160a01b03881687308b613b9c565b6001600160a01b038088166000908152600860205260409020541693508315612b2157836001600160a01b031663f7683bbc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ada573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612afe9190614d13565b92508260000361294a5760405163577f86e160e11b815260040160405180910390fd5b6011546001600160a01b0316612b4a57604051630d5f109960e21b815260040160405180910390fd5b60115460405163095ea7b360e01b81526001600160a01b038981169263095ea7b392612b7e92909116908c90600401614da9565b6020604051808303816000875af1158015612b9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bc19190614d59565b506011546040516322ceb11360e21b81526001600160a01b0390911690638b3ac44c90612bf4908a908c90600401614da9565b6020604051808303816000875af1158015612c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c379190614d13565b6011546040516332d2e94960e21b81529193506001600160a01b03169063cb4ba52490612c6a908a908c90600401614da9565b602060405180830381865afa158015612c87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cab9190614d13565b821015612ccb5760405163a3b7d2e560e01b815260040160405180910390fd5b506007546001600160a01b03165b6001600160a01b0380821660009081526008602052604090205416989297509095509350915050565b600081612d1784670de0b6b3a7640000614dc2565b612d219190614dd9565b90505b92915050565b6000612d5e6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60a08401516001600160a01b031660009081526015602052604090205460608201526080840151612e0b57612df8856040518060e0016040528087600001518152602001600015158152602001600081526020018760a001516001600160a01b0316815260200187608001511515815260200187610100015115158152602001612df088600001518960200151612d02565b9052856132fe565b5050604084015260208301528152612e92565b612e1d84600001518560200151612d02565b8082526060820151612e2e91613bd5565b6060820151612e4590670de0b6b3a7640000614994565b612e4f9190614dfb565b8151612e6490670de0b6b3a764000090614dc2565b612e6e9190614dd9565b8082528451612e7c91612d02565b60208201528351612e8c90613bea565b60408201525b80604001518160200151612ea69190614994565b608082015280516020820151612ebf9187918787613c0d565b6000612ecf868360600151613dac565b600e54909150600090612eea906001600160a01b0316613fb4565b9050806001600160a01b031663b5ac8d98604051806101600160405280858152602001896000015181526020018660400151815260200186600001518152602001306001600160a01b0316815260200189606001516001600160a01b031681526020018960a001516001600160a01b03168152602001600b60009054906101000a90046001600160a01b03166001600160a01b031663b3ee14246040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fb3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fd79190614d13565b612fe19042614994565b81526020018960800151151581526020018860ff16815260200189610100015115158152506040518263ffffffff1660e01b81526004016130229190614f3d565b600060405180830381600087803b15801561303c57600080fd5b505af1158015613050573d6000803e3d6000fd5b5050600a54606089015160405163514dd41d60e11b81526001600160a01b03909216935063a29ba83a925061308b918b918691600401615180565b600060405180830381600087803b1580156130a557600080fd5b505af11580156130b9573d6000803e3d6000fd5b505050508560c001516001600160a01b03166358c09cc082886000015186608001516130e59190614dfb565b6040518363ffffffff1660e01b8152600401613102929190614da9565b600060405180830381600087803b15801561311c57600080fd5b505af1158015613130573d6000803e3d6000fd5b505050608084015160a088015161315392506001600160a01b0316908390613b44565b608083015160405163242c6c2d60e01b815260048101919091526001600160a01b0382169063242c6c2d90602401600060405180830381600087803b15801561319b57600080fd5b505af11580156131af573d6000803e3d6000fd5b505050507fece8b280c05fcdb1e6f073f39266f576f9ee1f91bfe098dbb5f7b5e3dce305ba8282886000015186602001518a608001516040516131f69594939291906151b3565b60405180910390a16060868101518751604086810151602080890151895160a0808f015186516001600160a01b038c81168252998a16958101959095528487019790975297830193909352608082015294850152911660c0830152517fddd6ef4a731d70ec093d7f6a82160e586a40dde73b44129dcb771a5b64557d8a9181900360e00190a1925050505b9392505050565b60006132948484613bd5565b90506009548210156132ca57816009546132ae9190614dfb565b6132b990600a6152d9565b6132c39082614dc2565b9050613281565b600954821115613281576009546132e19083614dfb565b6132ec90600a6152d9565b6132f69082614dd9565b949350505050565b6000806000606060008088519050806001600160401b03811115613324576133246143fc565b60405190808252806020026020018201604052801561334d578160200160208202803683370190505b5092506000600b60009054906101000a90046001600160a01b03166001600160a01b031663e88698bf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133c99190614d13565b60608a01516001600160a01b0316600090815260156020526040812054919250600160ff8b161191905b848110156136065760008d828151811061340f5761340f614d76565b602090810291909101810151600b548151600090815260069093526040928390205492516311b3db5760e11b81529193506001600160a01b031691632367b6ae9161345e9185916004016152e5565b60006040518083038186803b15801561347657600080fd5b505afa15801561348a573d6000803e3d6000fd5b5050505080610120015160ff168160e0015151116134bb5760405163673f032f60e11b815260040160405180910390fd5b60008160e0015182610120015160ff16815181106134db576134db614d76565b602002602001015190506134ef8482613bd5565b61350185670de0b6b3a7640000614994565b61350b9190614dfb565b61351d670de0b6b3a764000083614dc2565b6135279190614dd9565b8e519091506135368183612d02565b6135409190614dfb565b89848151811061355257613552614d76565b60200260200101818152505084156135da57613576670de0b6b3a764000088614dc2565b8d60ff16670de0b6b3a76400008b868151811061359557613595614d76565b60200260200101516135a79190614dc2565b6135b19190614dc2565b6135bb9190614dd9565b8984815181106135cd576135cd614d76565b6020026020010181815250505b8b156135ef576135ea8c82613bd5565b6135f1565b805b9b505050806135ff90615307565b90506133f3565b5088156138515781156136a757600b60009054906101000a90046001600160a01b03166001600160a01b031663ff527ee38d8c8e60000151856040518563ffffffff1660e01b815260040161365e9493929190615320565b6040805180830381865afa15801561367a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061369e9190615352565b9950975061370c565b8a60a00151156136fd578a60c0015198506136c2818a613bd5565b6136d482670de0b6b3a7640000614994565b6136de9190614dfb565b6136f0670de0b6b3a76400008b614dc2565b6136fa9190614dd9565b98505b8a51613709908a612d02565b97505b82891015613728578298506137258b600001518a612d02565b97505b8a5161373390613bea565b96508a6020015115613851576060600b60009054906101000a90046001600160a01b03166001600160a01b031663c360aded8e8e604001518f608001518f6040518563ffffffff1660e01b81526004016137909493929190615376565b600060405180830381865afa1580156137ad573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526137d591908101906153a8565b909650905060005b8581101561382d578181815181106137f7576137f7614d76565b60200260200101511561382557600088828151811061381857613818614d76565b6020026020010181815250505b6001016137dd565b506000866002811115613842576138426145f1565b1461384f57600098508899505b505b50505050939792965093509350565b600d54604051630293b59d60e31b8152600091829182916001600160a01b03169063149dace890613895908890600401614284565b602060405180830381865afa1580156138b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138d69190614d8c565b90506000846001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016139069190614284565b602060405180830381865afa158015613923573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139479190614d13565b90506001600160a01b0382161580159061396f57506014546001600160a01b03878116911614155b15613a7d57600d5460405163c7d1f5f160e01b81526000916001600160a01b03169063c7d1f5f1906139a5908690600401614284565b602060405180830381865afa1580156139c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139e69190614d13565b90508015613a7b576139f88882613bd5565b9350838210613a7b57613a156001600160a01b0387168486613b44565b604080516001600160a01b0385811682528981166020830152818301879052606082018b90528816608082015290517f2f60e9c3fac363f0b03efb55d381f3d47475dbf1230e99201c9cdf4b9f577fc79181900360a00190a1613a788483614dfb565b91505b505b613a8687613bea565b935082841115613b3a576000613a9c8486614dfb565b9050808210613b38576001600160a01b0380871660009081526016602052604090205416613aec81613ad957600f546001600160a01b0316613adb565b815b6001600160a01b0389169084613b44565b60105460408051918252602082018490526001600160a01b03891682820152517f05b8bddc7eed21b7839baa291868ed966dd7299b79495dadd406c782b9770fe79181900360600190a1505b505b5050509392505050565b610f0f83846001600160a01b031663a9059cbb8585604051602401613b6a929190614da9565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614026565b6040516001600160a01b038481166024830152838116604483015260648201839052611fc49186918216906323b872dd90608401613b6a565b6000670de0b6b3a7640000612d178385614dc2565b6000670de0b6b3a764000060105483613c039190614dc2565b612d249190614dd9565b815160e083015160208401518115613cb25760008560a001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c879190614d13565b9050613c94848483613288565b9350613ca1878483613288565b9650613cae828483613288565b9150505b600b5460808601516101008701516040516354c0545160e01b81526001600160a01b03909316926354c0545192613cf6928d9289928d9290918c9190600401615458565b600060405180830381600087803b158015613d1057600080fd5b505af1158015613d24573d6000803e3d6000fd5b5050600b546040888101518c5191516318542d5360e31b815260048101899052602481018d9052604481018c905260648101879052608481019190915260a48101919091526001600160a01b03909116925063c2a16a98915060c40160006040518083038186803b158015613d9857600080fd5b505afa158015612899573d6000803e3d6000fd5b8151606090806001600160401b03811115613dc957613dc96143fc565b604051908082528060200260200182016040528015613e4a57816020015b60408051610140810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e083018290526101008301919091526101208201528252600019909201910181613de75790505b50915060005b81811015611d64576000858281518110613e6c57613e6c614d76565b6020026020010151905060008160e0015182610120015160ff1681518110613e9657613e96614d76565b6020026020010151905060405180610140016040528083600001518152602001836020015161ffff168152602001836040015161ffff16815260200183606001518152602001836080015160ff1681526020018360a0015160020b81526020018360c0015162ffffff16815260200183610120015160ff168152602001613f1d8884613bd5565b613f2f89670de0b6b3a7640000614994565b613f399190614dfb565b613f4b670de0b6b3a764000085614dc2565b613f559190614dd9565b815260200183610140015184610120015160ff1681518110613f7957613f79614d76565b6020026020010151815250858481518110613f9657613f96614d76565b6020026020010181905250505080613fad90615307565b9050613e50565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c176000526e5af43d82803e903d91602b57fd5bf38260781b17602052603760096000f090506001600160a01b038116614021576040516330be1a3d60e21b815260040160405180910390fd5b919050565b600061403b6001600160a01b03841683614080565b9050805160001415801561406057508080602001905181019061405e9190614d59565b155b15610f0f5782604051635274afe760e01b81526004016108fc9190614284565b6060612d218383600084600080856001600160a01b031684866040516140a69190615499565b60006040518083038185875af1925050503d80600081146140e3576040519150601f19603f3d011682016040523d82523d6000602084013e6140e8565b606091505b50915091506140f8868383614102565b9695505050505050565b6060826141175761411282614155565b613281565b815115801561412e57506001600160a01b0384163b155b1561414e5783604051639996b31560e01b81526004016108fc9190614284565b5080613281565b8051156141655780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60008083601f84011261419057600080fd5b5081356001600160401b038111156141a757600080fd5b6020830191508360208260051b85010111156141c257600080fd5b9250929050565b6001600160a01b0381168114610bb457600080fd5b8015158114610bb457600080fd5b60008060008060008060008060e0898b03121561420857600080fd5b88356001600160401b0381111561421e57600080fd5b61422a8b828c0161417e565b9099509750506020890135955060408901359450606089013593506080890135614253816141c9565b925060a0890135614263816141c9565b915060c0890135614273816141de565b809150509295985092959890939650565b6001600160a01b0391909116815260200190565b6000602082840312156142aa57600080fd5b8135613281816141c9565b6000602082840312156142c757600080fd5b8135613281816141de565b600080600080600080600060c0888a0312156142ed57600080fd5b87356001600160401b0381111561430357600080fd5b61430f8a828b0161417e565b90985096505060208801359450604088013593506060880135614331816141c9565b92506080880135614341816141c9565b915060a0880135614351816141c9565b8091505092959891949750929550565b60006020828403121561437357600080fd5b5035919050565b60008060008060008060c0878903121561439357600080fd5b863561439e816141c9565b955060208701356143ae816141c9565b945060408701356143be816141c9565b935060608701356143ce816141c9565b925060808701356143de816141c9565b915060a08701356143ee816141c9565b809150509295509295509295565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715614434576144346143fc565b60405290565b60405161016081016001600160401b0381118282101715614434576144346143fc565b604051601f8201601f191681016001600160401b0381118282101715614485576144856143fc565b604052919050565b60006001600160401b038211156144a6576144a66143fc565b5060051b60200190565b600082601f8301126144c157600080fd5b813560206144d66144d18361448d565b61445d565b82815260059290921b840181019181810190868411156144f557600080fd5b8286015b8481101561451057803583529183019183016144f9565b509695505050505050565b6000806040838503121561452e57600080fd5b82356001600160401b038082111561454557600080fd5b614551868387016144b0565b9350602085013591508082111561456757600080fd5b50614574858286016144b0565b9150509250929050565b60008060008060006080868803121561459657600080fd5b85356001600160401b038111156145ac57600080fd5b6145b88882890161417e565b9096509450506020860135925060408601356145d3816141c9565b915060608601356145e3816141de565b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600060c082018883526020888185015287604085015260c0606085015281875180845260e086019150828901935060005b8181101561465457845183529383019391830191600101614638565b505080935050505083608083015260038310614672576146726145f1565b8260a0830152979650505050505050565b803560ff8116811461402157600080fd5b60008060008060008060008060006101008a8c0312156146b357600080fd5b89356001600160401b038111156146c957600080fd5b6146d58c828d0161417e565b909a5098505060208a0135965060408a0135955060608a0135945060808a01356146fe816141c9565b935060a08a013561470e816141c9565b925060c08a013561471e816141de565b915061472c60e08b01614683565b90509295985092959850929598565b6000806040838503121561474e57600080fd5b50508035926020909101359150565b6000806040838503121561477057600080fd5b823561477b816141c9565b9150602083013561478b816141c9565b809150509250929050565b60008060008060008060a087890312156147af57600080fd5b86356001600160401b038111156147c557600080fd5b6147d189828a0161417e565b9097509550506020870135935060408701356147ec816141c9565b925060608701356147fc816141de565b915061480a60808801614683565b90509295509295509295565b60008060006060848603121561482b57600080fd5b8335614836816141c9565b92506020840135614846816141c9565b91506040840135614856816141c9565b809150509250925092565b6000806020838503121561487457600080fd5b82356001600160401b0381111561488a57600080fd5b6148968582860161417e565b90969095509350505050565b600080600080608085870312156148b857600080fd5b84356148c3816141c9565b935060208501356148d3816141c9565b92506040850135915060608501356148ea816141c9565b939692955090935050565b60038110610bb457600080fd5b6000806040838503121561491557600080fd5b8235614920816141c9565b9150602083013561478b816148f5565b600081518084526020808501945080840160005b8381101561496057815187529582019590820190600101614944565b509495945050505050565b602081526000612d216020830184614930565b634e487b7160e01b600052601160045260246000fd5b80820180821115612d2457612d2461497e565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b803561ffff8116811461402157600080fd5b8035600281900b811461402157600080fd5b803562ffffff8116811461402157600080fd5b600082601f830112614a4c57600080fd5b81356020614a5c6144d18361448d565b82815260059290921b84018101918181019086841115614a7b57600080fd5b8286015b848110156145105780356001600160401b03811115614a9e5760008081fd5b8701603f81018913614ab05760008081fd5b848101356040614ac26144d18361448d565b8281526060928302840182019288820191908d851115614ae25760008081fd5b948301945b84861015614b3f5780868f031215614aff5760008081fd5b614b07614412565b614b1087614a04565b8152614b1d8b8801614683565b8b820152614b2c858801614a16565b8186015283529485019491890191614ae7565b50875250505092840192508301614a7f565b6000614b5f6144d18461448d565b80848252602080830192508560051b850136811115614b7d57600080fd5b855b81811015614cb65780356001600160401b0380821115614b9f5760008081fd5b81890191506101608236031215614bb65760008081fd5b614bbe61443a565b82358152614bcd868401614a04565b868201526040614bde818501614a04565b90820152606083810135908201526080614bf9818501614683565b9082015260a0614c0a848201614a16565b9082015260c0614c1b848201614a28565b9082015260e08381013583811115614c335760008081fd5b614c3f368287016144b0565b8284015250506101008084013583811115614c5a5760008081fd5b614c66368287016144b0565b828401525050610120614c7a818501614683565b908201526101408381013583811115614c935760008081fd5b614c9f36828701614a3b565b918301919091525087525050938201938201614b7f565b50919695505050505050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6001600160a01b0392831681529116602082015260400190565b600060208284031215614d2557600080fd5b5051919050565b6001600160a01b03831681526040810160048310614d4c57614d4c6145f1565b8260208301529392505050565b600060208284031215614d6b57600080fd5b8151613281816141de565b634e487b7160e01b600052603260045260246000fd5b600060208284031215614d9e57600080fd5b8151613281816141c9565b6001600160a01b03929092168252602082015260400190565b8082028115828204841417612d2457612d2461497e565b600082614df657634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115612d2457612d2461497e565b600081518084526020808501945080840160005b83811015614960578151805161ffff1688528381015160ff168489015260409081015160020b9088015260609096019590820190600101614e22565b600081518084526020808501808196508360051b8101915082860160005b85811015614f3057828403895281516101408151865286820151614ea58888018261ffff169052565b5060408281015161ffff16908701526060808301519087015260808083015160ff169087015260a080830151600281900b82890152505060c08281015162ffffff169087015260e08083015160ff1690870152610100808301519087015261012091820151918601819052614f1c81870183614e0e565b9a87019a9550505090840190600101614e7c565b5091979650505050505050565b6020815260008251610160806020850152614f5c610180850183614e5e565b91506020850151604085015260408501516060850152606085015160808501526080850151614f9660a08601826001600160a01b03169052565b5060a08501516001600160a01b03811660c08601525060c08501516001600160a01b03811660e08601525060e085015161010085810191909152850151610120614fe38187018315159052565b8601519050610140614ff98682018360ff169052565b90950151151593019290925250919050565b600081518084526020808501808196508360051b8101915082860160005b85811015614f30578284038952615041848351614e0e565b98850198935090840190600101615029565b6000610160825184526020830151615071602086018261ffff169052565b506040830151615087604086018261ffff169052565b506060830151606085015260808301516150a6608086018260ff169052565b5060a08301516150bb60a086018260020b9052565b5060c08301516150d260c086018262ffffff169052565b5060e08301518160e08601526150ea82860182614930565b91505061010080840151858303828701526151058382614930565b925050506101208084015161511e8287018260ff169052565b505061014080840151858303828701526140f8838261500b565b600081518084526020808501808196508360051b8101915082860160005b85811015614f3057828403895261516e848351615053565b98850198935090840190600101615156565b6060815260006151936060830186615138565b6001600160a01b0394851660208401529290931660409091015292915050565b60a0815260006151c660a0830188614e5e565b6001600160a01b0396909616602083015250604081019390935260608301919091521515608090910152919050565b600181815b808511156152305781600019048211156152165761521661497e565b8085161561522357918102915b93841c93908002906151fa565b509250929050565b60008261524757506001612d24565b8161525457506000612d24565b816001811461526a576002811461527457615290565b6001915050612d24565b60ff8411156152855761528561497e565b50506001821b612d24565b5060208310610133831016604e8410600b84101617156152b3575081810a612d24565b6152bd83836151f5565b80600019048211156152d1576152d161497e565b029392505050565b6000612d218383615238565b6040815260006152f86040830185615053565b90508260208301529392505050565b6000600182016153195761531961497e565b5060010190565b6080815260006153336080830187615138565b60ff959095166020830152506040810192909252606090910152919050565b6000806040838503121561536557600080fd5b505080516020909101519092909150565b6080815260006153896080830187615138565b602083019590955250911515604083015260ff16606090910152919050565b600080604083850312156153bb57600080fd5b82516153c6816148f5565b809250506020808401516001600160401b038111156153e457600080fd5b8401601f810186136153f557600080fd5b80516154036144d18261448d565b81815260059190911b8201830190838101908883111561542257600080fd5b928401925b8284101561544957835161543a816141de565b82529284019290840190615427565b80955050505050509250929050565b60c08152600061546b60c0830189615138565b6020830197909752506040810194909452911515606084015260ff166080830152151560a090910152919050565b6000825160005b818110156154ba57602081860181015185830152016154a0565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca26469706673582212208ae5bf94451bbaf677710762c1eb3aa1b4da9b210f2d85bd2f75c169140e556164736f6c63430008140033
Deployed Bytecode
0x60806040526004361061028c5760003560e01c806391b4ded91161015a578063c3b83f5f116100c1578063e6d1c8a61161007a578063e6d1c8a6146107ed578063ebc797721461080d578063f453ea7d14610822578063f5bf372e14610842578063f8a940b01461086f578063fd47e5ec1461088f57600080fd5b8063c3b83f5f1461072d578063c4d66de81461074d578063ce18a8371461076d578063d3dc75391461078d578063d43b3576146107ad578063e481a342146107cd57600080fd5b8063aeb0f16411610113578063aeb0f16414610657578063b4e7e5c614610677578063b6a3670414610697578063b8e15ed0146106b7578063c2c9e276146106ed578063c301086c1461070d57600080fd5b806391b4ded9146105ae57806393b0c2ae146105c4578063942b67dc146105d75780639a23be78146105f75780639a618c0f14610617578063ace81b711461063757600080fd5b806347842663116101fe5780636cfb6bf9116101b75780636cfb6bf9146104e75780636dbf6cc7146105075780637649226c1461052757806379ba5097146105595780638da5cb5b1461056e5780638ff9d5cf1461058e57600080fd5b80634784266314610410578063481c6a751461043057806348663e951461045057806350631ddc1461047057806353a47bb71461049d5780635c975abb146104bd57600080fd5b80631627540c116102505780631627540c1461034357806316c38b3c14610363578063177957a81461038357806329cf5368146103b957806338ea7103146103cc57806344cab7ee146103f057600080fd5b8063014e17b01461029857806304de96c5146102c15780630ca08207146102e15780630fe621901461030357806313af40351461032357600080fd5b3661029357005b600080fd5b6102ab6102a63660046141ec565b6108bc565b6040516102b89190614284565b60405180910390f35b3480156102cd57600080fd5b506013546102ab906001600160a01b031681565b3480156102ed57600080fd5b506103016102fc366004614298565b61096b565b005b34801561030f57600080fd5b506017546102ab906001600160a01b031681565b34801561032f57600080fd5b5061030161033e366004614298565b6109e6565b34801561034f57600080fd5b5061030161035e366004614298565b610af2565b34801561036f57600080fd5b5061030161037e3660046142b5565b610b45565b34801561038f57600080fd5b506102ab61039e366004614298565b6008602052600090815260409020546001600160a01b031681565b6102ab6103c73660046142d2565b610bb7565b3480156103d857600080fd5b506103e260105481565b6040519081526020016102b8565b3480156103fc57600080fd5b506018546102ab906001600160a01b031681565b34801561041c57600080fd5b50600b546102ab906001600160a01b031681565b34801561043c57600080fd5b50600a546102ab906001600160a01b031681565b34801561045c57600080fd5b50600f546102ab906001600160a01b031681565b34801561047c57600080fd5b506103e261048b366004614361565b60066020526000908152604090205481565b3480156104a957600080fd5b506001546102ab906001600160a01b031681565b3480156104c957600080fd5b506003546104d79060ff1681565b60405190151581526020016102b8565b3480156104f357600080fd5b5061030161050236600461437a565b610cb1565b34801561051357600080fd5b5061030161052236600461451b565b610df3565b34801561053357600080fd5b5061054761054236600461457e565b610f14565b6040516102b896959493929190614607565b34801561056557600080fd5b50610301610f52565b34801561057a57600080fd5b506000546102ab906001600160a01b031681565b34801561059a57600080fd5b50600c546102ab906001600160a01b031681565b3480156105ba57600080fd5b506103e260025481565b6102ab6105d2366004614694565b61102a565b3480156105e357600080fd5b506103016105f236600461473b565b611108565b34801561060357600080fd5b5061030161061236600461475d565b6111c0565b34801561062357600080fd5b506011546102ab906001600160a01b031681565b34801561064357600080fd5b50610547610652366004614796565b61122a565b34801561066357600080fd5b506007546102ab906001600160a01b031681565b34801561068357600080fd5b50610301610692366004614816565b611269565b3480156106a357600080fd5b50600e546102ab906001600160a01b031681565b3480156106c357600080fd5b506102ab6106d2366004614298565b6016602052600090815260409020546001600160a01b031681565b3480156106f957600080fd5b50610301610708366004614861565b6112f5565b34801561071957600080fd5b50610301610728366004614298565b611497565b34801561073957600080fd5b50610301610748366004614298565b6114ea565b34801561075957600080fd5b50610301610768366004614298565b6115da565b34801561077957600080fd5b506014546102ab906001600160a01b031681565b34801561079957600080fd5b50600d546102ab906001600160a01b031681565b3480156107b957600080fd5b506103016107c83660046148a2565b6116e4565b3480156107d957600080fd5b506102ab6107e83660046142d2565b6117ee565b3480156107f957600080fd5b5061030161080836600461475d565b6118c1565b34801561081957600080fd5b50610301611a4e565b34801561082e57600080fd5b5061030161083d366004614361565b611aac565b34801561084e57600080fd5b506103e261085d366004614298565b60156020526000908152604090205481565b34801561087b57600080fd5b5061030161088a366004614902565b611ae9565b34801561089b57600080fd5b506108af6108aa366004614861565b611cc1565b6040516102b8919061496b565b60006001600460008282546108d19190614994565b909155505060045460035460ff16156109055760405162461bcd60e51b81526004016108fc906149a7565b60405180910390fd5b61093b6109128a8c614b51565b60408051606081018252600080825260208201819052918101919091528a8a8a8a8a8a33611d6c565b9150600454811461095e5760405162461bcd60e51b81526004016108fc90614cc2565b5098975050505050505050565b610973611e4b565b600754601154610990916001600160a01b03908116911683611ebf565b601180546001600160a01b0319166001600160a01b0383161790556040517f7d16d7e66eb2263f48c2c24af20e095395796ce3d05df64d84d9991a4d436247906109db908390614284565b60405180910390a150565b6001600160a01b038116610a385760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064016108fc565b600154600160a01b900460ff1615610aa45760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b60648201526084016108fc565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b03199091161781556040516000805160206154c9833981519152916109db918490614cf9565b610afa611e4b565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906109db908390614284565b610b4d611e4b565b60035460ff16151581151514610bb4576003805460ff191682151590811790915560ff1615610b7b57426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec5906020016109db565b50565b6000600160046000828254610bcc9190614994565b909155505060045460035460ff1615610bf75760405162461bcd60e51b81526004016108fc906149a7565b846001600160a01b038116610c1f576040516328f6040160e21b815260040160405180910390fd5b6018546001600160a01b03163314610c4a57604051637bcec4fb60e01b815260040160405180910390fd5b610c81610c578a8c614b51565b60408051606081018252600080825260016020830152918101829052908b908b908a8a828e611d6c565b9250506004548114610ca55760405162461bcd60e51b81526004016108fc90614cc2565b50979650505050505050565b610cb9611e4b565b600780546001600160a01b0319166001600160a01b0388169081179091556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa158015610d12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d369190614d13565b600955600a80546001600160a01b038781166001600160a01b03199283168117909355600b80548883169084168117909155600c80548884169085168117909155600d80548885169086168117909155600f805488861696168617905560408051948d1685526020850196909652948301919091526060820152608081019290925260a08201527f1962de5c532b26a8a72c8524fe745d05421f16a6521fc531ce010572f742a6939060c0015b60405180910390a1505050505050565b600a5460405163e760c39560e01b81526001600160a01b039091169063e760c39590610e26903390600090600401614d2c565b602060405180830381865afa158015610e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e679190614d59565b158015610e7f57506000546001600160a01b03163314155b15610e9d57604051636edaef2f60e11b815260040160405180910390fd5b8051825114610ebf5760405163251f56a160e21b815260040160405180910390fd5b60005b8251811015610f0f57610f07838281518110610ee057610ee0614d76565b6020026020010151838381518110610efa57610efa614d76565b6020026020010151611fca565b600101610ec2565b505050565b60008060006060600080610f2d8b8b8b8b8b6000612019565b809650819750829850839950849a50859b505050505050509550955095509550955095565b6001546001600160a01b03163314610fca5760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b60648201526084016108fc565b6000546001546040516000805160206154c983398151915292610ffb926001600160a01b0391821692911690614cf9565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b600060016004600082825461103f9190614994565b909155505060045460035460ff161561106a5760405162461bcd60e51b81526004016108fc906149a7565b60018360ff16111580611080575060ff83168a11155b1561109e57604051631a0a722b60e01b815260040160405180910390fd5b6110d76110ab8b8d614b51565b6040805160608101825260ff87168152600060208201819052918101919091528b8b8b8b8b8b33611d6c565b915060045481146110fa5760405162461bcd60e51b81526004016108fc90614cc2565b509998505050505050505050565b600a5460405163e760c39560e01b81526001600160a01b039091169063e760c3959061113b903390600090600401614d2c565b602060405180830381865afa158015611158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117c9190614d59565b15801561119457506000546001600160a01b03163314155b156111b257604051636edaef2f60e11b815260040160405180910390fd5b6111bc8282611fca565b5050565b6111c8611e4b565b604051634946980760e11b81526001600160a01b0383169063928d300e906111f4908490600401614284565b600060405180830381600087803b15801561120e57600080fd5b505af1158015611222573d6000803e3d6000fd5b505050505050565b600080600060606000806112428c8c8c8c8c8c612019565b809650819750829850839950849a50859b5050505050505096509650965096509650969050565b611271611e4b565b601380546001600160a01b038581166001600160a01b0319928316811790935560188054868316908416811790915560148054928616929093168217909255604080519384526020840192909252908201527f2b8bc92a77df3bf6abc793bbc13c813f4e0028dd6de7aefb204a96ade4ddc4739060600160405180910390a1505050565b6112fd611e4b565b60005b81811015610f0f57600083838381811061131c5761131c614d76565b90506020020160208101906113319190614298565b90506000816001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611372573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113969190614d8c565b6014549091506001600160a01b038083169116148015906113c557506017546001600160a01b03828116911614155b1561148d5760405163646d919f60e11b81526001600160a01b0383169063c8db233e906113f6903390600401614284565b600060405180830381600087803b15801561141057600080fd5b505af1158015611424573d6000803e3d6000fd5b5050600a5460405163f033d0f960e01b81526001600160a01b03909116925063f033d0f9915061145a9085908590600401614cf9565b600060405180830381600087803b15801561147457600080fd5b505af1158015611488573d6000803e3d6000fd5b505050505b5050600101611300565b61149f611e4b565b600e80546001600160a01b0319166001600160a01b0383161790556040517fab496e3942a33c5a729be5985af46209e18077a462af550b750246460950eb7f906109db908390614284565b6114f2611e4b565b6001600160a01b03811661153a5760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016108fc565b600154600160a81b900460ff161561158a5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b60448201526064016108fc565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b1790556040516000805160206154c9833981519152916109db918490614cf9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b031660008115801561161f5750825b90506000826001600160401b0316600114801561163b5750303b155b905081158015611649575080155b156116675760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561169157845460ff60401b1916600160401b1785555b61169a866109e6565b6116a2611a4e565b831561122257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610de3565b6116ec611e4b565b6001600160a01b03848116600090815260086020526040902054811690841614611766576001600160a01b038085166000908152600860205260409020546117379186911685611ebf565b6001600160a01b03848116600090815260086020526040902080546001600160a01b0319169185169190911790555b6001600160a01b038481166000818152601560209081526040808320879055601682529182902080546001600160a01b031916868616908117909155825193845293871690830152810184905260608101919091527f2083b2a6d9c12b8cd65a8a4d92a82708aac25a81a524e9d13e92559bf818db569060800160405180910390a150505050565b60006001600460008282546118039190614994565b909155505060045460035460ff161561182e5760405162461bcd60e51b81526004016108fc906149a7565b846001600160a01b038116611856576040516328f6040160e21b815260040160405180910390fd5b6013546001600160a01b0316331461188157604051637bcec4fb60e01b815260040160405180910390fd5b610c8161188e8a8c614b51565b6040518060600160405280600060ff168152602001600015158152602001600115158152508a8a60008a8a60008e611d6c565b6001600460008282546118d49190614994565b909155505060045460035460ff16156118ff5760405162461bcd60e51b81526004016108fc906149a7565b600a546040516321875b5760e01b815284916001600160a01b0316906321875b579061192f908490600401614284565b602060405180830381865afa15801561194c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119709190614d59565b61198d57604051634f8c20e160e01b815260040160405180910390fd5b836001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ee9190614d8c565b6001600160a01b0316336001600160a01b031614611a1f5760405163bba02e7160e01b815260040160405180910390fd5b611a2c8484600080612264565b506004548114610f0f5760405162461bcd60e51b81526004016108fc90614cc2565b60055460ff1615611a975760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016108fc565b6005805460ff19166001908117909155600455565b611ab4611e4b565b60108190556040518181527f5da87bc83066ddf563cfd9de25e562860f147b358751f50766847996202e5d33906020016109db565b600160046000828254611afc9190614994565b909155505060045460035460ff1615611b275760405162461bcd60e51b81526004016108fc906149a7565b600a546040516321875b5760e01b815284916001600160a01b0316906321875b5790611b57908490600401614284565b602060405180830381865afa158015611b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b989190614d59565b611bb557604051634f8c20e160e01b815260040160405180910390fd5b6000836002811115611bc957611bc96145f1565b14611c5f57600a5460405163e760c39560e01b81526001600160a01b039091169063e760c39590611c01903390600290600401614d2c565b602060405180830381865afa158015611c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c429190614d59565b611c5f57604051637830359760e01b815260040160405180910390fd5b6001836002811115611c7357611c736145f1565b03611c8c57611c8784600060016000612264565b611a2c565b6002836002811115611ca057611ca06145f1565b03611cb357611c87846000806001612264565b611a2c846000806000612264565b606081806001600160401b03811115611cdc57611cdc6143fc565b604051908082528060200260200182016040528015611d05578160200160208202803683370190505b50915060005b81811015611d645760066000868684818110611d2957611d29614d76565b90506020020135815260200190815260200160002054838281518110611d5157611d51614d76565b6020908102919091010152600101611d0b565b505092915050565b6000861580611d79575087155b15611d975760405163d1892e7760e01b815260040160405180910390fd5b611da185836128a7565b600080611db08a878688612904565b604080516101208101909152828152919d5098509193509150611e3b908d9060208101611ddd8e8e612d02565b81526020018b8152602001876001600160a01b031681526020018e6040015115158152602001896001600160a01b03168152602001856001600160a01b031681526020018481526020018e6020015115158152508d60000151612d2a565b9c9b505050505050505050505050565b6000546001600160a01b03163314611ebd5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b60648201526084016108fc565b565b6001600160a01b03821615611f425760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390611efd908590600090600401614da9565b6020604051808303816000875af1158015611f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f409190614d59565b505b6001600160a01b03811615610f0f5760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390611f8190849060001990600401614da9565b6020604051808303816000875af1158015611fa0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc49190614d59565b50505050565b60008281526006602090815260409182902083905581518481529081018390527fc6fa3d673d901ef180e5a314ff8ede38ac8ba226ce71c9d822ed8a438020a1ab910160405180910390a15050565b6000808060608782816001600160a01b038a161580159061204857506007546001600160a01b038b8116911614155b156121cd576001600160a01b03808b1660009081526008602052604090205416806120fb57601160009054906101000a90046001600160a01b03166001600160a01b031663cb4ba5248c8e6040518363ffffffff1660e01b81526004016120b0929190614da9565b602060405180830381865afa1580156120cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f19190614d13565b93508391506121cb565b6121c88c826001600160a01b031663f7683bbc6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561213d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121619190614d13565b8d6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561219f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c39190614d13565b613288565b93505b505b806000036121ee57604051631f2a200560e01b815260040160405180910390fd5b6122406121fb8d8f614b51565b6040805160e081018252848152600160208201529081018690526001600160a01b038d1660608201528b15156080820152600060a0820181905260c08201528a6132fe565b809650819850829950839a50849b5050505050505096509650965096509650969050565b83600082156122d857816001600160a01b0316636a265aa96040518163ffffffff1660e01b81526004016020604051808303816000875af11580156122ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d19190614d13565b9050612390565b831561231e57816001600160a01b031663ea8a1af06040518163ffffffff1660e01b81526004016020604051808303816000875af11580156122ad573d6000803e3d6000fd5b604051630d8acc1560e11b81526001600160a01b03831690631b15982a9061234a908890600401614284565b6020604051808303816000875af1158015612369573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238d9190614d13565b90505b6000826001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f49190614d8c565b90506000836001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612435573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124599190614d8c565b6014549091506001600160a01b038083169116148061248557506017546001600160a01b038281169116145b156124e95760405163509eb5b560e11b81526001600160a01b0382169063a13d6b6a906124b6908b90600401614284565b600060405180830381600087803b1580156124d057600080fd5b505af11580156124e4573d6000803e3d6000fd5b505050505b836001600160a01b0316639a82a09a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612527573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061254b9190614d59565b6125bd576125bb846001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa158015612590573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b49190614d13565b8284613860565b505b600a546040516306ef036760e01b81526001600160a01b03909116906306ef0367906125ef908b908590600401614cf9565b600060405180830381600087803b15801561260957600080fd5b505af115801561261d573d6000803e3d6000fd5b505050507f1a208f07d825103c5b1eb3bb1109b420fd544123300beb9e220fc951cbd7dd0b8882866001600160a01b0316633356a35a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612682573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126a69190614d59565b604080516001600160a01b03948516815293909216602084015215159082015260600160405180910390a16000831180156126e957506001600160a01b03871615155b80156127075750816001600160a01b0316876001600160a01b031614155b156127c6576007546001600160a01b0383811691161461273a5760405163c6338d5560e01b815260040160405180910390fd5b601154604051630992646d60e31b81526127c69183916001600160a01b0390911690634c93236890612772908c908990600401614da9565b6020604051808303816000875af1158015612791573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b59190614d13565b6001600160a01b038a169190613b44565b6001600160a01b03808316600081815260086020526040908190205490516370a0823160e01b815292169163d7efa129918b916370a082319061280d903090600401614284565b602060405180830381865afa15801561282a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284e9190614d13565b6040518363ffffffff1660e01b815260040161286b929190614da9565b600060405180830381600087803b15801561288557600080fd5b505af1158015612899573d6000803e3d6000fd5b505050505050505050505050565b6001600160a01b038216158015906128cd57506014546001600160a01b03828116911614155b156111bc57600d5460405163bbddaca360e01b81526001600160a01b039091169063bbddaca3906111f49085908590600401614cf9565b60008085816001600160a01b038716158061292c57506007546001600160a01b038881169116145b1561294f57506007546001600160a01b031661294a8187308b613b9c565b612cd9565b50858415612a6457601160009054906101000a90046001600160a01b03166001600160a01b0316634aa4a4fc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156129aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129ce9190614d8c565b6001600160a01b0316876001600160a01b03161415806129ed57508734105b15612a0b57604051634f4d1d8760e01b815260040160405180910390fd5b866001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015612a4657600080fd5b505af1158015612a5a573d6000803e3d6000fd5b5050505050612a79565b612a796001600160a01b03881687308b613b9c565b6001600160a01b038088166000908152600860205260409020541693508315612b2157836001600160a01b031663f7683bbc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ada573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612afe9190614d13565b92508260000361294a5760405163577f86e160e11b815260040160405180910390fd5b6011546001600160a01b0316612b4a57604051630d5f109960e21b815260040160405180910390fd5b60115460405163095ea7b360e01b81526001600160a01b038981169263095ea7b392612b7e92909116908c90600401614da9565b6020604051808303816000875af1158015612b9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bc19190614d59565b506011546040516322ceb11360e21b81526001600160a01b0390911690638b3ac44c90612bf4908a908c90600401614da9565b6020604051808303816000875af1158015612c13573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c379190614d13565b6011546040516332d2e94960e21b81529193506001600160a01b03169063cb4ba52490612c6a908a908c90600401614da9565b602060405180830381865afa158015612c87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cab9190614d13565b821015612ccb5760405163a3b7d2e560e01b815260040160405180910390fd5b506007546001600160a01b03165b6001600160a01b0380821660009081526008602052604090205416989297509095509350915050565b600081612d1784670de0b6b3a7640000614dc2565b612d219190614dd9565b90505b92915050565b6000612d5e6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60a08401516001600160a01b031660009081526015602052604090205460608201526080840151612e0b57612df8856040518060e0016040528087600001518152602001600015158152602001600081526020018760a001516001600160a01b0316815260200187608001511515815260200187610100015115158152602001612df088600001518960200151612d02565b9052856132fe565b5050604084015260208301528152612e92565b612e1d84600001518560200151612d02565b8082526060820151612e2e91613bd5565b6060820151612e4590670de0b6b3a7640000614994565b612e4f9190614dfb565b8151612e6490670de0b6b3a764000090614dc2565b612e6e9190614dd9565b8082528451612e7c91612d02565b60208201528351612e8c90613bea565b60408201525b80604001518160200151612ea69190614994565b608082015280516020820151612ebf9187918787613c0d565b6000612ecf868360600151613dac565b600e54909150600090612eea906001600160a01b0316613fb4565b9050806001600160a01b031663b5ac8d98604051806101600160405280858152602001896000015181526020018660400151815260200186600001518152602001306001600160a01b0316815260200189606001516001600160a01b031681526020018960a001516001600160a01b03168152602001600b60009054906101000a90046001600160a01b03166001600160a01b031663b3ee14246040518163ffffffff1660e01b8152600401602060405180830381865afa158015612fb3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fd79190614d13565b612fe19042614994565b81526020018960800151151581526020018860ff16815260200189610100015115158152506040518263ffffffff1660e01b81526004016130229190614f3d565b600060405180830381600087803b15801561303c57600080fd5b505af1158015613050573d6000803e3d6000fd5b5050600a54606089015160405163514dd41d60e11b81526001600160a01b03909216935063a29ba83a925061308b918b918691600401615180565b600060405180830381600087803b1580156130a557600080fd5b505af11580156130b9573d6000803e3d6000fd5b505050508560c001516001600160a01b03166358c09cc082886000015186608001516130e59190614dfb565b6040518363ffffffff1660e01b8152600401613102929190614da9565b600060405180830381600087803b15801561311c57600080fd5b505af1158015613130573d6000803e3d6000fd5b505050608084015160a088015161315392506001600160a01b0316908390613b44565b608083015160405163242c6c2d60e01b815260048101919091526001600160a01b0382169063242c6c2d90602401600060405180830381600087803b15801561319b57600080fd5b505af11580156131af573d6000803e3d6000fd5b505050507fece8b280c05fcdb1e6f073f39266f576f9ee1f91bfe098dbb5f7b5e3dce305ba8282886000015186602001518a608001516040516131f69594939291906151b3565b60405180910390a16060868101518751604086810151602080890151895160a0808f015186516001600160a01b038c81168252998a16958101959095528487019790975297830193909352608082015294850152911660c0830152517fddd6ef4a731d70ec093d7f6a82160e586a40dde73b44129dcb771a5b64557d8a9181900360e00190a1925050505b9392505050565b60006132948484613bd5565b90506009548210156132ca57816009546132ae9190614dfb565b6132b990600a6152d9565b6132c39082614dc2565b9050613281565b600954821115613281576009546132e19083614dfb565b6132ec90600a6152d9565b6132f69082614dd9565b949350505050565b6000806000606060008088519050806001600160401b03811115613324576133246143fc565b60405190808252806020026020018201604052801561334d578160200160208202803683370190505b5092506000600b60009054906101000a90046001600160a01b03166001600160a01b031663e88698bf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156133a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133c99190614d13565b60608a01516001600160a01b0316600090815260156020526040812054919250600160ff8b161191905b848110156136065760008d828151811061340f5761340f614d76565b602090810291909101810151600b548151600090815260069093526040928390205492516311b3db5760e11b81529193506001600160a01b031691632367b6ae9161345e9185916004016152e5565b60006040518083038186803b15801561347657600080fd5b505afa15801561348a573d6000803e3d6000fd5b5050505080610120015160ff168160e0015151116134bb5760405163673f032f60e11b815260040160405180910390fd5b60008160e0015182610120015160ff16815181106134db576134db614d76565b602002602001015190506134ef8482613bd5565b61350185670de0b6b3a7640000614994565b61350b9190614dfb565b61351d670de0b6b3a764000083614dc2565b6135279190614dd9565b8e519091506135368183612d02565b6135409190614dfb565b89848151811061355257613552614d76565b60200260200101818152505084156135da57613576670de0b6b3a764000088614dc2565b8d60ff16670de0b6b3a76400008b868151811061359557613595614d76565b60200260200101516135a79190614dc2565b6135b19190614dc2565b6135bb9190614dd9565b8984815181106135cd576135cd614d76565b6020026020010181815250505b8b156135ef576135ea8c82613bd5565b6135f1565b805b9b505050806135ff90615307565b90506133f3565b5088156138515781156136a757600b60009054906101000a90046001600160a01b03166001600160a01b031663ff527ee38d8c8e60000151856040518563ffffffff1660e01b815260040161365e9493929190615320565b6040805180830381865afa15801561367a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061369e9190615352565b9950975061370c565b8a60a00151156136fd578a60c0015198506136c2818a613bd5565b6136d482670de0b6b3a7640000614994565b6136de9190614dfb565b6136f0670de0b6b3a76400008b614dc2565b6136fa9190614dd9565b98505b8a51613709908a612d02565b97505b82891015613728578298506137258b600001518a612d02565b97505b8a5161373390613bea565b96508a6020015115613851576060600b60009054906101000a90046001600160a01b03166001600160a01b031663c360aded8e8e604001518f608001518f6040518563ffffffff1660e01b81526004016137909493929190615376565b600060405180830381865afa1580156137ad573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526137d591908101906153a8565b909650905060005b8581101561382d578181815181106137f7576137f7614d76565b60200260200101511561382557600088828151811061381857613818614d76565b6020026020010181815250505b6001016137dd565b506000866002811115613842576138426145f1565b1461384f57600098508899505b505b50505050939792965093509350565b600d54604051630293b59d60e31b8152600091829182916001600160a01b03169063149dace890613895908890600401614284565b602060405180830381865afa1580156138b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138d69190614d8c565b90506000846001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016139069190614284565b602060405180830381865afa158015613923573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139479190614d13565b90506001600160a01b0382161580159061396f57506014546001600160a01b03878116911614155b15613a7d57600d5460405163c7d1f5f160e01b81526000916001600160a01b03169063c7d1f5f1906139a5908690600401614284565b602060405180830381865afa1580156139c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139e69190614d13565b90508015613a7b576139f88882613bd5565b9350838210613a7b57613a156001600160a01b0387168486613b44565b604080516001600160a01b0385811682528981166020830152818301879052606082018b90528816608082015290517f2f60e9c3fac363f0b03efb55d381f3d47475dbf1230e99201c9cdf4b9f577fc79181900360a00190a1613a788483614dfb565b91505b505b613a8687613bea565b935082841115613b3a576000613a9c8486614dfb565b9050808210613b38576001600160a01b0380871660009081526016602052604090205416613aec81613ad957600f546001600160a01b0316613adb565b815b6001600160a01b0389169084613b44565b60105460408051918252602082018490526001600160a01b03891682820152517f05b8bddc7eed21b7839baa291868ed966dd7299b79495dadd406c782b9770fe79181900360600190a1505b505b5050509392505050565b610f0f83846001600160a01b031663a9059cbb8585604051602401613b6a929190614da9565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050614026565b6040516001600160a01b038481166024830152838116604483015260648201839052611fc49186918216906323b872dd90608401613b6a565b6000670de0b6b3a7640000612d178385614dc2565b6000670de0b6b3a764000060105483613c039190614dc2565b612d249190614dd9565b815160e083015160208401518115613cb25760008560a001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c63573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c879190614d13565b9050613c94848483613288565b9350613ca1878483613288565b9650613cae828483613288565b9150505b600b5460808601516101008701516040516354c0545160e01b81526001600160a01b03909316926354c0545192613cf6928d9289928d9290918c9190600401615458565b600060405180830381600087803b158015613d1057600080fd5b505af1158015613d24573d6000803e3d6000fd5b5050600b546040888101518c5191516318542d5360e31b815260048101899052602481018d9052604481018c905260648101879052608481019190915260a48101919091526001600160a01b03909116925063c2a16a98915060c40160006040518083038186803b158015613d9857600080fd5b505afa158015612899573d6000803e3d6000fd5b8151606090806001600160401b03811115613dc957613dc96143fc565b604051908082528060200260200182016040528015613e4a57816020015b60408051610140810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e083018290526101008301919091526101208201528252600019909201910181613de75790505b50915060005b81811015611d64576000858281518110613e6c57613e6c614d76565b6020026020010151905060008160e0015182610120015160ff1681518110613e9657613e96614d76565b6020026020010151905060405180610140016040528083600001518152602001836020015161ffff168152602001836040015161ffff16815260200183606001518152602001836080015160ff1681526020018360a0015160020b81526020018360c0015162ffffff16815260200183610120015160ff168152602001613f1d8884613bd5565b613f2f89670de0b6b3a7640000614994565b613f399190614dfb565b613f4b670de0b6b3a764000085614dc2565b613f559190614dd9565b815260200183610140015184610120015160ff1681518110613f7957613f79614d76565b6020026020010151815250858481518110613f9657613f96614d76565b6020026020010181905250505080613fad90615307565b9050613e50565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c176000526e5af43d82803e903d91602b57fd5bf38260781b17602052603760096000f090506001600160a01b038116614021576040516330be1a3d60e21b815260040160405180910390fd5b919050565b600061403b6001600160a01b03841683614080565b9050805160001415801561406057508080602001905181019061405e9190614d59565b155b15610f0f5782604051635274afe760e01b81526004016108fc9190614284565b6060612d218383600084600080856001600160a01b031684866040516140a69190615499565b60006040518083038185875af1925050503d80600081146140e3576040519150601f19603f3d011682016040523d82523d6000602084013e6140e8565b606091505b50915091506140f8868383614102565b9695505050505050565b6060826141175761411282614155565b613281565b815115801561412e57506001600160a01b0384163b155b1561414e5783604051639996b31560e01b81526004016108fc9190614284565b5080613281565b8051156141655780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60008083601f84011261419057600080fd5b5081356001600160401b038111156141a757600080fd5b6020830191508360208260051b85010111156141c257600080fd5b9250929050565b6001600160a01b0381168114610bb457600080fd5b8015158114610bb457600080fd5b60008060008060008060008060e0898b03121561420857600080fd5b88356001600160401b0381111561421e57600080fd5b61422a8b828c0161417e565b9099509750506020890135955060408901359450606089013593506080890135614253816141c9565b925060a0890135614263816141c9565b915060c0890135614273816141de565b809150509295985092959890939650565b6001600160a01b0391909116815260200190565b6000602082840312156142aa57600080fd5b8135613281816141c9565b6000602082840312156142c757600080fd5b8135613281816141de565b600080600080600080600060c0888a0312156142ed57600080fd5b87356001600160401b0381111561430357600080fd5b61430f8a828b0161417e565b90985096505060208801359450604088013593506060880135614331816141c9565b92506080880135614341816141c9565b915060a0880135614351816141c9565b8091505092959891949750929550565b60006020828403121561437357600080fd5b5035919050565b60008060008060008060c0878903121561439357600080fd5b863561439e816141c9565b955060208701356143ae816141c9565b945060408701356143be816141c9565b935060608701356143ce816141c9565b925060808701356143de816141c9565b915060a08701356143ee816141c9565b809150509295509295509295565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715614434576144346143fc565b60405290565b60405161016081016001600160401b0381118282101715614434576144346143fc565b604051601f8201601f191681016001600160401b0381118282101715614485576144856143fc565b604052919050565b60006001600160401b038211156144a6576144a66143fc565b5060051b60200190565b600082601f8301126144c157600080fd5b813560206144d66144d18361448d565b61445d565b82815260059290921b840181019181810190868411156144f557600080fd5b8286015b8481101561451057803583529183019183016144f9565b509695505050505050565b6000806040838503121561452e57600080fd5b82356001600160401b038082111561454557600080fd5b614551868387016144b0565b9350602085013591508082111561456757600080fd5b50614574858286016144b0565b9150509250929050565b60008060008060006080868803121561459657600080fd5b85356001600160401b038111156145ac57600080fd5b6145b88882890161417e565b9096509450506020860135925060408601356145d3816141c9565b915060608601356145e3816141de565b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600060c082018883526020888185015287604085015260c0606085015281875180845260e086019150828901935060005b8181101561465457845183529383019391830191600101614638565b505080935050505083608083015260038310614672576146726145f1565b8260a0830152979650505050505050565b803560ff8116811461402157600080fd5b60008060008060008060008060006101008a8c0312156146b357600080fd5b89356001600160401b038111156146c957600080fd5b6146d58c828d0161417e565b909a5098505060208a0135965060408a0135955060608a0135945060808a01356146fe816141c9565b935060a08a013561470e816141c9565b925060c08a013561471e816141de565b915061472c60e08b01614683565b90509295985092959850929598565b6000806040838503121561474e57600080fd5b50508035926020909101359150565b6000806040838503121561477057600080fd5b823561477b816141c9565b9150602083013561478b816141c9565b809150509250929050565b60008060008060008060a087890312156147af57600080fd5b86356001600160401b038111156147c557600080fd5b6147d189828a0161417e565b9097509550506020870135935060408701356147ec816141c9565b925060608701356147fc816141de565b915061480a60808801614683565b90509295509295509295565b60008060006060848603121561482b57600080fd5b8335614836816141c9565b92506020840135614846816141c9565b91506040840135614856816141c9565b809150509250925092565b6000806020838503121561487457600080fd5b82356001600160401b0381111561488a57600080fd5b6148968582860161417e565b90969095509350505050565b600080600080608085870312156148b857600080fd5b84356148c3816141c9565b935060208501356148d3816141c9565b92506040850135915060608501356148ea816141c9565b939692955090935050565b60038110610bb457600080fd5b6000806040838503121561491557600080fd5b8235614920816141c9565b9150602083013561478b816148f5565b600081518084526020808501945080840160005b8381101561496057815187529582019590820190600101614944565b509495945050505050565b602081526000612d216020830184614930565b634e487b7160e01b600052601160045260246000fd5b80820180821115612d2457612d2461497e565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b803561ffff8116811461402157600080fd5b8035600281900b811461402157600080fd5b803562ffffff8116811461402157600080fd5b600082601f830112614a4c57600080fd5b81356020614a5c6144d18361448d565b82815260059290921b84018101918181019086841115614a7b57600080fd5b8286015b848110156145105780356001600160401b03811115614a9e5760008081fd5b8701603f81018913614ab05760008081fd5b848101356040614ac26144d18361448d565b8281526060928302840182019288820191908d851115614ae25760008081fd5b948301945b84861015614b3f5780868f031215614aff5760008081fd5b614b07614412565b614b1087614a04565b8152614b1d8b8801614683565b8b820152614b2c858801614a16565b8186015283529485019491890191614ae7565b50875250505092840192508301614a7f565b6000614b5f6144d18461448d565b80848252602080830192508560051b850136811115614b7d57600080fd5b855b81811015614cb65780356001600160401b0380821115614b9f5760008081fd5b81890191506101608236031215614bb65760008081fd5b614bbe61443a565b82358152614bcd868401614a04565b868201526040614bde818501614a04565b90820152606083810135908201526080614bf9818501614683565b9082015260a0614c0a848201614a16565b9082015260c0614c1b848201614a28565b9082015260e08381013583811115614c335760008081fd5b614c3f368287016144b0565b8284015250506101008084013583811115614c5a5760008081fd5b614c66368287016144b0565b828401525050610120614c7a818501614683565b908201526101408381013583811115614c935760008081fd5b614c9f36828701614a3b565b918301919091525087525050938201938201614b7f565b50919695505050505050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6001600160a01b0392831681529116602082015260400190565b600060208284031215614d2557600080fd5b5051919050565b6001600160a01b03831681526040810160048310614d4c57614d4c6145f1565b8260208301529392505050565b600060208284031215614d6b57600080fd5b8151613281816141de565b634e487b7160e01b600052603260045260246000fd5b600060208284031215614d9e57600080fd5b8151613281816141c9565b6001600160a01b03929092168252602082015260400190565b8082028115828204841417612d2457612d2461497e565b600082614df657634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115612d2457612d2461497e565b600081518084526020808501945080840160005b83811015614960578151805161ffff1688528381015160ff168489015260409081015160020b9088015260609096019590820190600101614e22565b600081518084526020808501808196508360051b8101915082860160005b85811015614f3057828403895281516101408151865286820151614ea58888018261ffff169052565b5060408281015161ffff16908701526060808301519087015260808083015160ff169087015260a080830151600281900b82890152505060c08281015162ffffff169087015260e08083015160ff1690870152610100808301519087015261012091820151918601819052614f1c81870183614e0e565b9a87019a9550505090840190600101614e7c565b5091979650505050505050565b6020815260008251610160806020850152614f5c610180850183614e5e565b91506020850151604085015260408501516060850152606085015160808501526080850151614f9660a08601826001600160a01b03169052565b5060a08501516001600160a01b03811660c08601525060c08501516001600160a01b03811660e08601525060e085015161010085810191909152850151610120614fe38187018315159052565b8601519050610140614ff98682018360ff169052565b90950151151593019290925250919050565b600081518084526020808501808196508360051b8101915082860160005b85811015614f30578284038952615041848351614e0e565b98850198935090840190600101615029565b6000610160825184526020830151615071602086018261ffff169052565b506040830151615087604086018261ffff169052565b506060830151606085015260808301516150a6608086018260ff169052565b5060a08301516150bb60a086018260020b9052565b5060c08301516150d260c086018262ffffff169052565b5060e08301518160e08601526150ea82860182614930565b91505061010080840151858303828701526151058382614930565b925050506101208084015161511e8287018260ff169052565b505061014080840151858303828701526140f8838261500b565b600081518084526020808501808196508360051b8101915082860160005b85811015614f3057828403895261516e848351615053565b98850198935090840190600101615156565b6060815260006151936060830186615138565b6001600160a01b0394851660208401529290931660409091015292915050565b60a0815260006151c660a0830188614e5e565b6001600160a01b0396909616602083015250604081019390935260608301919091521515608090910152919050565b600181815b808511156152305781600019048211156152165761521661497e565b8085161561522357918102915b93841c93908002906151fa565b509250929050565b60008261524757506001612d24565b8161525457506000612d24565b816001811461526a576002811461527457615290565b6001915050612d24565b60ff8411156152855761528561497e565b50506001821b612d24565b5060208310610133831016604e8410600b84101617156152b3575081810a612d24565b6152bd83836151f5565b80600019048211156152d1576152d161497e565b029392505050565b6000612d218383615238565b6040815260006152f86040830185615053565b90508260208301529392505050565b6000600182016153195761531961497e565b5060010190565b6080815260006153336080830187615138565b60ff959095166020830152506040810192909252606090910152919050565b6000806040838503121561536557600080fd5b505080516020909101519092909150565b6080815260006153896080830187615138565b602083019590955250911515604083015260ff16606090910152919050565b600080604083850312156153bb57600080fd5b82516153c6816148f5565b809250506020808401516001600160401b038111156153e457600080fd5b8401601f810186136153f557600080fd5b80516154036144d18261448d565b81815260059190911b8201830190838101908883111561542257600080fd5b928401925b8284101561544957835161543a816141de565b82529284019290840190615427565b80955050505050509250929050565b60c08152600061546b60c0830189615138565b6020830197909752506040810194909452911515606084015260ff166080830152151560a090910152919050565b6000825160005b818110156154ba57602081860181015185830152016154a0565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca26469706673582212208ae5bf94451bbaf677710762c1eb3aa1b4da9b210f2d85bd2f75c169140e556164736f6c63430008140033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.