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));
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
)
);
manager.addNewKnownTicket(_tradeData, address(ticket), _tradeDataInternal._recipient);
ISportsAMMV2LiquidityPool(_tradeDataInternal._collateralPool).commitTrade(
address(ticket),
processingParams._payoutWithFees - _tradeDataInternal._buyInAmount
);
IERC20(_tradeDataInternal._collateral).safeTransfer(address(ticket), 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)) {
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)) 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;
/* ========== CONSTRUCTOR ========== */
/// @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;
}
/* ========== 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");
uint payoutWithFees = collateral.balanceOf(address(this));
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);
}// 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 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"
]
}
},
"libraries": {}
}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
608060405234801561001057600080fd5b50615481806100206000396000f3fe60806040526004361061028c5760003560e01c806391b4ded91161015a578063c3b83f5f116100c1578063e6d1c8a61161007a578063e6d1c8a6146107ed578063ebc797721461080d578063f453ea7d14610822578063f5bf372e14610842578063f8a940b01461086f578063fd47e5ec1461088f57600080fd5b8063c3b83f5f1461072d578063c4d66de81461074d578063ce18a8371461076d578063d3dc75391461078d578063d43b3576146107ad578063e481a342146107cd57600080fd5b8063aeb0f16411610113578063aeb0f16414610657578063b4e7e5c614610677578063b6a3670414610697578063b8e15ed0146106b7578063c2c9e276146106ed578063c301086c1461070d57600080fd5b806391b4ded9146105ae57806393b0c2ae146105c4578063942b67dc146105d75780639a23be78146105f75780639a618c0f14610617578063ace81b711461063757600080fd5b806347842663116101fe5780636cfb6bf9116101b75780636cfb6bf9146104e75780636dbf6cc7146105075780637649226c1461052757806379ba5097146105595780638da5cb5b1461056e5780638ff9d5cf1461058e57600080fd5b80634784266314610410578063481c6a751461043057806348663e951461045057806350631ddc1461047057806353a47bb71461049d5780635c975abb146104bd57600080fd5b80631627540c116102505780631627540c1461034357806316c38b3c14610363578063177957a81461038357806329cf5368146103b957806338ea7103146103cc57806344cab7ee146103f057600080fd5b8063014e17b01461029857806304de96c5146102c15780630ca08207146102e15780630fe621901461030357806313af40351461032357600080fd5b3661029357005b600080fd5b6102ab6102a636600461414f565b6108bc565b6040516102b891906141e7565b60405180910390f35b3480156102cd57600080fd5b506013546102ab906001600160a01b031681565b3480156102ed57600080fd5b506103016102fc3660046141fb565b61096b565b005b34801561030f57600080fd5b506017546102ab906001600160a01b031681565b34801561032f57600080fd5b5061030161033e3660046141fb565b6109e6565b34801561034f57600080fd5b5061030161035e3660046141fb565b610af2565b34801561036f57600080fd5b5061030161037e366004614218565b610b45565b34801561038f57600080fd5b506102ab61039e3660046141fb565b6008602052600090815260409020546001600160a01b031681565b6102ab6103c7366004614235565b610bb7565b3480156103d857600080fd5b506103e260105481565b6040519081526020016102b8565b3480156103fc57600080fd5b506018546102ab906001600160a01b031681565b34801561041c57600080fd5b50600b546102ab906001600160a01b031681565b34801561043c57600080fd5b50600a546102ab906001600160a01b031681565b34801561045c57600080fd5b50600f546102ab906001600160a01b031681565b34801561047c57600080fd5b506103e261048b3660046142c4565b60066020526000908152604090205481565b3480156104a957600080fd5b506001546102ab906001600160a01b031681565b3480156104c957600080fd5b506003546104d79060ff1681565b60405190151581526020016102b8565b3480156104f357600080fd5b506103016105023660046142dd565b610cb1565b34801561051357600080fd5b5061030161052236600461447e565b610df3565b34801561053357600080fd5b506105476105423660046144e1565b610f14565b6040516102b89695949392919061456a565b34801561056557600080fd5b50610301610f52565b34801561057a57600080fd5b506000546102ab906001600160a01b031681565b34801561059a57600080fd5b50600c546102ab906001600160a01b031681565b3480156105ba57600080fd5b506103e260025481565b6102ab6105d23660046145f7565b61102a565b3480156105e357600080fd5b506103016105f236600461469e565b611108565b34801561060357600080fd5b506103016106123660046146c0565b6111c0565b34801561062357600080fd5b506011546102ab906001600160a01b031681565b34801561064357600080fd5b506105476106523660046146f9565b61122a565b34801561066357600080fd5b506007546102ab906001600160a01b031681565b34801561068357600080fd5b50610301610692366004614779565b611269565b3480156106a357600080fd5b50600e546102ab906001600160a01b031681565b3480156106c357600080fd5b506102ab6106d23660046141fb565b6016602052600090815260409020546001600160a01b031681565b3480156106f957600080fd5b506103016107083660046147c4565b6112f5565b34801561071957600080fd5b506103016107283660046141fb565b611497565b34801561073957600080fd5b506103016107483660046141fb565b6114ea565b34801561075957600080fd5b506103016107683660046141fb565b6115da565b34801561077957600080fd5b506014546102ab906001600160a01b031681565b34801561079957600080fd5b50600d546102ab906001600160a01b031681565b3480156107b957600080fd5b506103016107c8366004614805565b6116e4565b3480156107d957600080fd5b506102ab6107e8366004614235565b6117ee565b3480156107f957600080fd5b506103016108083660046146c0565b6118c1565b34801561081957600080fd5b50610301611a4e565b34801561082e57600080fd5b5061030161083d3660046142c4565b611aac565b34801561084e57600080fd5b506103e261085d3660046141fb565b60156020526000908152604090205481565b34801561087b57600080fd5b5061030161088a366004614865565b611ae9565b34801561089b57600080fd5b506108af6108aa3660046147c4565b611cc1565b6040516102b891906148ce565b60006001600460008282546108d191906148f7565b909155505060045460035460ff16156109055760405162461bcd60e51b81526004016108fc9061490a565b60405180910390fd5b61093b6109128a8c614ab4565b60408051606081018252600080825260208201819052918101919091528a8a8a8a8a8a33611d6c565b9150600454811461095e5760405162461bcd60e51b81526004016108fc90614c25565b5098975050505050505050565b610973611e4b565b600754601154610990916001600160a01b03908116911683611ebf565b601180546001600160a01b0319166001600160a01b0383161790556040517f7d16d7e66eb2263f48c2c24af20e095395796ce3d05df64d84d9991a4d436247906109db9083906141e7565b60405180910390a150565b6001600160a01b038116610a385760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064016108fc565b600154600160a01b900460ff1615610aa45760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b60648201526084016108fc565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b031990911617815560405160008051602061542c833981519152916109db918490614c5c565b610afa611e4b565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906109db9083906141e7565b610b4d611e4b565b60035460ff16151581151514610bb4576003805460ff191682151590811790915560ff1615610b7b57426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec5906020016109db565b50565b6000600160046000828254610bcc91906148f7565b909155505060045460035460ff1615610bf75760405162461bcd60e51b81526004016108fc9061490a565b846001600160a01b038116610c1f576040516328f6040160e21b815260040160405180910390fd5b6018546001600160a01b03163314610c4a57604051637bcec4fb60e01b815260040160405180910390fd5b610c81610c578a8c614ab4565b60408051606081018252600080825260016020830152918101829052908b908b908a8a828e611d6c565b9250506004548114610ca55760405162461bcd60e51b81526004016108fc90614c25565b50979650505050505050565b610cb9611e4b565b600780546001600160a01b0319166001600160a01b0388169081179091556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa158015610d12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d369190614c76565b600955600a80546001600160a01b038781166001600160a01b03199283168117909355600b80548883169084168117909155600c80548884169085168117909155600d80548885169086168117909155600f805488861696168617905560408051948d1685526020850196909652948301919091526060820152608081019290925260a08201527f1962de5c532b26a8a72c8524fe745d05421f16a6521fc531ce010572f742a6939060c0015b60405180910390a1505050505050565b600a5460405163e760c39560e01b81526001600160a01b039091169063e760c39590610e26903390600090600401614c8f565b602060405180830381865afa158015610e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e679190614cbc565b158015610e7f57506000546001600160a01b03163314155b15610e9d57604051636edaef2f60e11b815260040160405180910390fd5b8051825114610ebf5760405163251f56a160e21b815260040160405180910390fd5b60005b8251811015610f0f57610f07838281518110610ee057610ee0614cd9565b6020026020010151838381518110610efa57610efa614cd9565b6020026020010151611fca565b600101610ec2565b505050565b60008060006060600080610f2d8b8b8b8b8b6000612019565b809650819750829850839950849a50859b505050505050509550955095509550955095565b6001546001600160a01b03163314610fca5760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b60648201526084016108fc565b60005460015460405160008051602061542c83398151915292610ffb926001600160a01b0391821692911690614c5c565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b600060016004600082825461103f91906148f7565b909155505060045460035460ff161561106a5760405162461bcd60e51b81526004016108fc9061490a565b60018360ff16111580611080575060ff83168a11155b1561109e57604051631a0a722b60e01b815260040160405180910390fd5b6110d76110ab8b8d614ab4565b6040805160608101825260ff87168152600060208201819052918101919091528b8b8b8b8b8b33611d6c565b915060045481146110fa5760405162461bcd60e51b81526004016108fc90614c25565b509998505050505050505050565b600a5460405163e760c39560e01b81526001600160a01b039091169063e760c3959061113b903390600090600401614c8f565b602060405180830381865afa158015611158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117c9190614cbc565b15801561119457506000546001600160a01b03163314155b156111b257604051636edaef2f60e11b815260040160405180910390fd5b6111bc8282611fca565b5050565b6111c8611e4b565b604051634946980760e11b81526001600160a01b0383169063928d300e906111f49084906004016141e7565b600060405180830381600087803b15801561120e57600080fd5b505af1158015611222573d6000803e3d6000fd5b505050505050565b600080600060606000806112428c8c8c8c8c8c612019565b809650819750829850839950849a50859b5050505050505096509650965096509650969050565b611271611e4b565b601380546001600160a01b038581166001600160a01b0319928316811790935560188054868316908416811790915560148054928616929093168217909255604080519384526020840192909252908201527f2b8bc92a77df3bf6abc793bbc13c813f4e0028dd6de7aefb204a96ade4ddc4739060600160405180910390a1505050565b6112fd611e4b565b60005b81811015610f0f57600083838381811061131c5761131c614cd9565b905060200201602081019061133191906141fb565b90506000816001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611372573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113969190614cef565b6014549091506001600160a01b038083169116148015906113c557506017546001600160a01b03828116911614155b1561148d5760405163646d919f60e11b81526001600160a01b0383169063c8db233e906113f69033906004016141e7565b600060405180830381600087803b15801561141057600080fd5b505af1158015611424573d6000803e3d6000fd5b5050600a5460405163f033d0f960e01b81526001600160a01b03909116925063f033d0f9915061145a9085908590600401614c5c565b600060405180830381600087803b15801561147457600080fd5b505af1158015611488573d6000803e3d6000fd5b505050505b5050600101611300565b61149f611e4b565b600e80546001600160a01b0319166001600160a01b0383161790556040517fab496e3942a33c5a729be5985af46209e18077a462af550b750246460950eb7f906109db9083906141e7565b6114f2611e4b565b6001600160a01b03811661153a5760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016108fc565b600154600160a81b900460ff161561158a5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b60448201526064016108fc565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b17905560405160008051602061542c833981519152916109db918490614c5c565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b031660008115801561161f5750825b90506000826001600160401b0316600114801561163b5750303b155b905081158015611649575080155b156116675760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561169157845460ff60401b1916600160401b1785555b61169a866109e6565b6116a2611a4e565b831561122257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610de3565b6116ec611e4b565b6001600160a01b03848116600090815260086020526040902054811690841614611766576001600160a01b038085166000908152600860205260409020546117379186911685611ebf565b6001600160a01b03848116600090815260086020526040902080546001600160a01b0319169185169190911790555b6001600160a01b038481166000818152601560209081526040808320879055601682529182902080546001600160a01b031916868616908117909155825193845293871690830152810184905260608101919091527f2083b2a6d9c12b8cd65a8a4d92a82708aac25a81a524e9d13e92559bf818db569060800160405180910390a150505050565b600060016004600082825461180391906148f7565b909155505060045460035460ff161561182e5760405162461bcd60e51b81526004016108fc9061490a565b846001600160a01b038116611856576040516328f6040160e21b815260040160405180910390fd5b6013546001600160a01b0316331461188157604051637bcec4fb60e01b815260040160405180910390fd5b610c8161188e8a8c614ab4565b6040518060600160405280600060ff168152602001600015158152602001600115158152508a8a60008a8a60008e611d6c565b6001600460008282546118d491906148f7565b909155505060045460035460ff16156118ff5760405162461bcd60e51b81526004016108fc9061490a565b600a546040516321875b5760e01b815284916001600160a01b0316906321875b579061192f9084906004016141e7565b602060405180830381865afa15801561194c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119709190614cbc565b61198d57604051634f8c20e160e01b815260040160405180910390fd5b836001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ee9190614cef565b6001600160a01b0316336001600160a01b031614611a1f5760405163bba02e7160e01b815260040160405180910390fd5b611a2c8484600080612264565b506004548114610f0f5760405162461bcd60e51b81526004016108fc90614c25565b60055460ff1615611a975760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016108fc565b6005805460ff19166001908117909155600455565b611ab4611e4b565b60108190556040518181527f5da87bc83066ddf563cfd9de25e562860f147b358751f50766847996202e5d33906020016109db565b600160046000828254611afc91906148f7565b909155505060045460035460ff1615611b275760405162461bcd60e51b81526004016108fc9061490a565b600a546040516321875b5760e01b815284916001600160a01b0316906321875b5790611b579084906004016141e7565b602060405180830381865afa158015611b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b989190614cbc565b611bb557604051634f8c20e160e01b815260040160405180910390fd5b6000836002811115611bc957611bc9614554565b14611c5f57600a5460405163e760c39560e01b81526001600160a01b039091169063e760c39590611c01903390600290600401614c8f565b602060405180830381865afa158015611c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c429190614cbc565b611c5f57604051637830359760e01b815260040160405180910390fd5b6001836002811115611c7357611c73614554565b03611c8c57611c8784600060016000612264565b611a2c565b6002836002811115611ca057611ca0614554565b03611cb357611c87846000806001612264565b611a2c846000806000612264565b606081806001600160401b03811115611cdc57611cdc61435f565b604051908082528060200260200182016040528015611d05578160200160208202803683370190505b50915060005b81811015611d645760066000868684818110611d2957611d29614cd9565b90506020020135815260200190815260200160002054838281518110611d5157611d51614cd9565b6020908102919091010152600101611d0b565b505092915050565b6000861580611d79575087155b15611d975760405163d1892e7760e01b815260040160405180910390fd5b611da185836128a7565b600080611db08a8786886128e8565b604080516101208101909152828152919d5098509193509150611e3b908d9060208101611ddd8e8e612ce6565b81526020018b8152602001876001600160a01b031681526020018e6040015115158152602001896001600160a01b03168152602001856001600160a01b031681526020018481526020018e6020015115158152508d60000151612d0e565b9c9b505050505050505050505050565b6000546001600160a01b03163314611ebd5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b60648201526084016108fc565b565b6001600160a01b03821615611f425760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390611efd908590600090600401614d0c565b6020604051808303816000875af1158015611f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f409190614cbc565b505b6001600160a01b03811615610f0f5760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390611f8190849060001990600401614d0c565b6020604051808303816000875af1158015611fa0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc49190614cbc565b50505050565b60008281526006602090815260409182902083905581518481529081018390527fc6fa3d673d901ef180e5a314ff8ede38ac8ba226ce71c9d822ed8a438020a1ab910160405180910390a15050565b6000808060608782816001600160a01b038a161580159061204857506007546001600160a01b038b8116911614155b156121cd576001600160a01b03808b1660009081526008602052604090205416806120fb57601160009054906101000a90046001600160a01b03166001600160a01b031663cb4ba5248c8e6040518363ffffffff1660e01b81526004016120b0929190614d0c565b602060405180830381865afa1580156120cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f19190614c76565b93508391506121cb565b6121c88c826001600160a01b031663f7683bbc6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561213d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121619190614c76565b8d6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561219f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c39190614c76565b613207565b93505b505b806000036121ee57604051631f2a200560e01b815260040160405180910390fd5b6122406121fb8d8f614ab4565b6040805160e081018252848152600160208201529081018690526001600160a01b038d1660608201528b15156080820152600060a0820181905260c08201528a61327d565b809650819850829950839a50849b5050505050505096509650965096509650969050565b83600082156122d857816001600160a01b0316636a265aa96040518163ffffffff1660e01b81526004016020604051808303816000875af11580156122ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d19190614c76565b9050612390565b831561231e57816001600160a01b031663ea8a1af06040518163ffffffff1660e01b81526004016020604051808303816000875af11580156122ad573d6000803e3d6000fd5b604051630d8acc1560e11b81526001600160a01b03831690631b15982a9061234a9088906004016141e7565b6020604051808303816000875af1158015612369573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238d9190614c76565b90505b6000826001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f49190614cef565b90506000836001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612435573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124599190614cef565b6014549091506001600160a01b038083169116148061248557506017546001600160a01b038281169116145b156124e95760405163509eb5b560e11b81526001600160a01b0382169063a13d6b6a906124b6908b906004016141e7565b600060405180830381600087803b1580156124d057600080fd5b505af11580156124e4573d6000803e3d6000fd5b505050505b836001600160a01b0316639a82a09a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612527573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061254b9190614cbc565b6125bd576125bb846001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa158015612590573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b49190614c76565b82846137df565b505b600a546040516306ef036760e01b81526001600160a01b03909116906306ef0367906125ef908b908590600401614c5c565b600060405180830381600087803b15801561260957600080fd5b505af115801561261d573d6000803e3d6000fd5b505050507f1a208f07d825103c5b1eb3bb1109b420fd544123300beb9e220fc951cbd7dd0b8882866001600160a01b0316633356a35a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612682573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126a69190614cbc565b604080516001600160a01b03948516815293909216602084015215159082015260600160405180910390a16000831180156126e957506001600160a01b03871615155b80156127075750816001600160a01b0316876001600160a01b031614155b156127c6576007546001600160a01b0383811691161461273a5760405163c6338d5560e01b815260040160405180910390fd5b601154604051630992646d60e31b81526127c69183916001600160a01b0390911690634c93236890612772908c908990600401614d0c565b6020604051808303816000875af1158015612791573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b59190614c76565b6001600160a01b038a169190613aa7565b6001600160a01b03808316600081815260086020526040908190205490516370a0823160e01b815292169163d7efa129918b916370a082319061280d9030906004016141e7565b602060405180830381865afa15801561282a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284e9190614c76565b6040518363ffffffff1660e01b815260040161286b929190614d0c565b600060405180830381600087803b15801561288557600080fd5b505af1158015612899573d6000803e3d6000fd5b505050505050505050505050565b6001600160a01b038216156111bc57600d5460405163bbddaca360e01b81526001600160a01b039091169063bbddaca3906111f49085908590600401614c5c565b60008085816001600160a01b038716158061291057506007546001600160a01b038881169116145b1561293357506007546001600160a01b031661292e8187308b613aff565b612cbd565b50858415612a4857601160009054906101000a90046001600160a01b03166001600160a01b0316634aa4a4fc6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561298e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129b29190614cef565b6001600160a01b0316876001600160a01b03161415806129d157508734105b156129ef57604051634f4d1d8760e01b815260040160405180910390fd5b866001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015612a2a57600080fd5b505af1158015612a3e573d6000803e3d6000fd5b5050505050612a5d565b612a5d6001600160a01b03881687308b613aff565b6001600160a01b038088166000908152600860205260409020541693508315612b0557836001600160a01b031663f7683bbc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612abe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae29190614c76565b92508260000361292e5760405163577f86e160e11b815260040160405180910390fd5b6011546001600160a01b0316612b2e57604051630d5f109960e21b815260040160405180910390fd5b60115460405163095ea7b360e01b81526001600160a01b038981169263095ea7b392612b6292909116908c90600401614d0c565b6020604051808303816000875af1158015612b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba59190614cbc565b506011546040516322ceb11360e21b81526001600160a01b0390911690638b3ac44c90612bd8908a908c90600401614d0c565b6020604051808303816000875af1158015612bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c1b9190614c76565b6011546040516332d2e94960e21b81529193506001600160a01b03169063cb4ba52490612c4e908a908c90600401614d0c565b602060405180830381865afa158015612c6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c8f9190614c76565b821015612caf5760405163a3b7d2e560e01b815260040160405180910390fd5b506007546001600160a01b03165b6001600160a01b0380821660009081526008602052604090205416989297509095509350915050565b600081612cfb84670de0b6b3a7640000614d25565b612d059190614d3c565b90505b92915050565b6000612d426040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60a08401516001600160a01b031660009081526015602052604090205460608201526080840151612def57612ddc856040518060e0016040528087600001518152602001600015158152602001600081526020018760a001516001600160a01b0316815260200187608001511515815260200187610100015115158152602001612dd488600001518960200151612ce6565b90528561327d565b5050604084015260208301528152612e76565b612e0184600001518560200151612ce6565b8082526060820151612e1291613b38565b6060820151612e2990670de0b6b3a76400006148f7565b612e339190614d5e565b8151612e4890670de0b6b3a764000090614d25565b612e529190614d3c565b8082528451612e6091612ce6565b60208201528351612e7090613b4d565b60408201525b80604001518160200151612e8a91906148f7565b608082015280516020820151612ea39187918787613b70565b6000612eb3868360600151613d0f565b600e54909150600090612ece906001600160a01b0316613f17565b9050806001600160a01b031663b5ac8d98604051806101600160405280858152602001896000015181526020018660400151815260200186600001518152602001306001600160a01b0316815260200189606001516001600160a01b031681526020018960a001516001600160a01b03168152602001600b60009054906101000a90046001600160a01b03166001600160a01b031663b3ee14246040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fbb9190614c76565b612fc590426148f7565b81526020018960800151151581526020018860ff16815260200189610100015115158152506040518263ffffffff1660e01b81526004016130069190614ea0565b600060405180830381600087803b15801561302057600080fd5b505af1158015613034573d6000803e3d6000fd5b5050600a54606089015160405163514dd41d60e11b81526001600160a01b03909216935063a29ba83a925061306f918b9186916004016150e3565b600060405180830381600087803b15801561308957600080fd5b505af115801561309d573d6000803e3d6000fd5b505050508560c001516001600160a01b03166358c09cc082886000015186608001516130c99190614d5e565b6040518363ffffffff1660e01b81526004016130e6929190614d0c565b600060405180830381600087803b15801561310057600080fd5b505af1158015613114573d6000803e3d6000fd5b505050608084015160a088015161313792506001600160a01b0316908390613aa7565b8551602084015160808801516040517fece8b280c05fcdb1e6f073f39266f576f9ee1f91bfe098dbb5f7b5e3dce305ba936131759387938793615116565b60405180910390a16060868101518751604086810151602080890151895160a0808f015186516001600160a01b038c81168252998a16958101959095528487019790975297830193909352608082015294850152911660c0830152517fddd6ef4a731d70ec093d7f6a82160e586a40dde73b44129dcb771a5b64557d8a9181900360e00190a1925050505b9392505050565b60006132138484613b38565b9050600954821015613249578160095461322d9190614d5e565b61323890600a61523c565b6132429082614d25565b9050613200565b600954821115613200576009546132609083614d5e565b61326b90600a61523c565b6132759082614d3c565b949350505050565b6000806000606060008088519050806001600160401b038111156132a3576132a361435f565b6040519080825280602002602001820160405280156132cc578160200160208202803683370190505b5092506000600b60009054906101000a90046001600160a01b03166001600160a01b031663e88698bf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613324573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133489190614c76565b60608a01516001600160a01b0316600090815260156020526040812054919250600160ff8b161191905b848110156135855760008d828151811061338e5761338e614cd9565b602090810291909101810151600b548151600090815260069093526040928390205492516311b3db5760e11b81529193506001600160a01b031691632367b6ae916133dd918591600401615248565b60006040518083038186803b1580156133f557600080fd5b505afa158015613409573d6000803e3d6000fd5b5050505080610120015160ff168160e00151511161343a5760405163673f032f60e11b815260040160405180910390fd5b60008160e0015182610120015160ff168151811061345a5761345a614cd9565b6020026020010151905061346e8482613b38565b61348085670de0b6b3a76400006148f7565b61348a9190614d5e565b61349c670de0b6b3a764000083614d25565b6134a69190614d3c565b8e519091506134b58183612ce6565b6134bf9190614d5e565b8984815181106134d1576134d1614cd9565b6020026020010181815250508415613559576134f5670de0b6b3a764000088614d25565b8d60ff16670de0b6b3a76400008b868151811061351457613514614cd9565b60200260200101516135269190614d25565b6135309190614d25565b61353a9190614d3c565b89848151811061354c5761354c614cd9565b6020026020010181815250505b8b1561356e576135698c82613b38565b613570565b805b9b5050508061357e9061526a565b9050613372565b5088156137d057811561362657600b60009054906101000a90046001600160a01b03166001600160a01b031663ff527ee38d8c8e60000151856040518563ffffffff1660e01b81526004016135dd9493929190615283565b6040805180830381865afa1580156135f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061361d91906152b5565b9950975061368b565b8a60a001511561367c578a60c001519850613641818a613b38565b61365382670de0b6b3a76400006148f7565b61365d9190614d5e565b61366f670de0b6b3a76400008b614d25565b6136799190614d3c565b98505b8a51613688908a612ce6565b97505b828910156136a7578298506136a48b600001518a612ce6565b97505b8a516136b290613b4d565b96508a60200151156137d0576060600b60009054906101000a90046001600160a01b03166001600160a01b031663c360aded8e8e604001518f608001518f6040518563ffffffff1660e01b815260040161370f94939291906152d9565b600060405180830381865afa15801561372c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613754919081019061530b565b909650905060005b858110156137ac5781818151811061377657613776614cd9565b6020026020010151156137a457600088828151811061379757613797614cd9565b6020026020010181815250505b60010161375c565b5060008660028111156137c1576137c1614554565b146137ce57600098508899505b505b50505050939792965093509350565b600d54604051630293b59d60e31b8152600091829182916001600160a01b03169063149dace8906138149088906004016141e7565b602060405180830381865afa158015613831573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138559190614cef565b90506000846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161388591906141e7565b602060405180830381865afa1580156138a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138c69190614c76565b90506001600160a01b038216156139e057600d5460405163c7d1f5f160e01b81526000916001600160a01b03169063c7d1f5f1906139089086906004016141e7565b602060405180830381865afa158015613925573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139499190614c76565b905080156139de5761395b8882613b38565b93508382106139de576139786001600160a01b0387168486613aa7565b604080516001600160a01b0385811682528981166020830152818301879052606082018b90528816608082015290517f2f60e9c3fac363f0b03efb55d381f3d47475dbf1230e99201c9cdf4b9f577fc79181900360a00190a16139db8483614d5e565b91505b505b6139e987613b4d565b935082841115613a9d5760006139ff8486614d5e565b9050808210613a9b576001600160a01b0380871660009081526016602052604090205416613a4f81613a3c57600f546001600160a01b0316613a3e565b815b6001600160a01b0389169084613aa7565b60105460408051918252602082018490526001600160a01b03891682820152517f05b8bddc7eed21b7839baa291868ed966dd7299b79495dadd406c782b9770fe79181900360600190a1505b505b5050509392505050565b610f0f83846001600160a01b031663a9059cbb8585604051602401613acd929190614d0c565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613f89565b6040516001600160a01b038481166024830152838116604483015260648201839052611fc49186918216906323b872dd90608401613acd565b6000670de0b6b3a7640000612cfb8385614d25565b6000670de0b6b3a764000060105483613b669190614d25565b612d089190614d3c565b815160e083015160208401518115613c155760008560a001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613bc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bea9190614c76565b9050613bf7848483613207565b9350613c04878483613207565b9650613c11828483613207565b9150505b600b5460808601516101008701516040516354c0545160e01b81526001600160a01b03909316926354c0545192613c59928d9289928d9290918c91906004016153bb565b600060405180830381600087803b158015613c7357600080fd5b505af1158015613c87573d6000803e3d6000fd5b5050600b546040888101518c5191516318542d5360e31b815260048101899052602481018d9052604481018c905260648101879052608481019190915260a48101919091526001600160a01b03909116925063c2a16a98915060c40160006040518083038186803b158015613cfb57600080fd5b505afa158015612899573d6000803e3d6000fd5b8151606090806001600160401b03811115613d2c57613d2c61435f565b604051908082528060200260200182016040528015613dad57816020015b60408051610140810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e083018290526101008301919091526101208201528252600019909201910181613d4a5790505b50915060005b81811015611d64576000858281518110613dcf57613dcf614cd9565b6020026020010151905060008160e0015182610120015160ff1681518110613df957613df9614cd9565b6020026020010151905060405180610140016040528083600001518152602001836020015161ffff168152602001836040015161ffff16815260200183606001518152602001836080015160ff1681526020018360a0015160020b81526020018360c0015162ffffff16815260200183610120015160ff168152602001613e808884613b38565b613e9289670de0b6b3a76400006148f7565b613e9c9190614d5e565b613eae670de0b6b3a764000085614d25565b613eb89190614d3c565b815260200183610140015184610120015160ff1681518110613edc57613edc614cd9565b6020026020010151815250858481518110613ef957613ef9614cd9565b6020026020010181905250505080613f109061526a565b9050613db3565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c176000526e5af43d82803e903d91602b57fd5bf38260781b17602052603760096000f090506001600160a01b038116613f84576040516330be1a3d60e21b815260040160405180910390fd5b919050565b6000613f9e6001600160a01b03841683613fe3565b90508051600014158015613fc3575080806020019051810190613fc19190614cbc565b155b15610f0f5782604051635274afe760e01b81526004016108fc91906141e7565b6060612d058383600084600080856001600160a01b0316848660405161400991906153fc565b60006040518083038185875af1925050503d8060008114614046576040519150601f19603f3d011682016040523d82523d6000602084013e61404b565b606091505b509150915061405b868383614065565b9695505050505050565b60608261407a57614075826140b8565b613200565b815115801561409157506001600160a01b0384163b155b156140b15783604051639996b31560e01b81526004016108fc91906141e7565b5080613200565b8051156140c85780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60008083601f8401126140f357600080fd5b5081356001600160401b0381111561410a57600080fd5b6020830191508360208260051b850101111561412557600080fd5b9250929050565b6001600160a01b0381168114610bb457600080fd5b8015158114610bb457600080fd5b60008060008060008060008060e0898b03121561416b57600080fd5b88356001600160401b0381111561418157600080fd5b61418d8b828c016140e1565b90995097505060208901359550604089013594506060890135935060808901356141b68161412c565b925060a08901356141c68161412c565b915060c08901356141d681614141565b809150509295985092959890939650565b6001600160a01b0391909116815260200190565b60006020828403121561420d57600080fd5b81356132008161412c565b60006020828403121561422a57600080fd5b813561320081614141565b600080600080600080600060c0888a03121561425057600080fd5b87356001600160401b0381111561426657600080fd5b6142728a828b016140e1565b909850965050602088013594506040880135935060608801356142948161412c565b925060808801356142a48161412c565b915060a08801356142b48161412c565b8091505092959891949750929550565b6000602082840312156142d657600080fd5b5035919050565b60008060008060008060c087890312156142f657600080fd5b86356143018161412c565b955060208701356143118161412c565b945060408701356143218161412c565b935060608701356143318161412c565b925060808701356143418161412c565b915060a08701356143518161412c565b809150509295509295509295565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b03811182821017156143975761439761435f565b60405290565b60405161016081016001600160401b03811182821017156143975761439761435f565b604051601f8201601f191681016001600160401b03811182821017156143e8576143e861435f565b604052919050565b60006001600160401b038211156144095761440961435f565b5060051b60200190565b600082601f83011261442457600080fd5b81356020614439614434836143f0565b6143c0565b82815260059290921b8401810191818101908684111561445857600080fd5b8286015b84811015614473578035835291830191830161445c565b509695505050505050565b6000806040838503121561449157600080fd5b82356001600160401b03808211156144a857600080fd5b6144b486838701614413565b935060208501359150808211156144ca57600080fd5b506144d785828601614413565b9150509250929050565b6000806000806000608086880312156144f957600080fd5b85356001600160401b0381111561450f57600080fd5b61451b888289016140e1565b9096509450506020860135925060408601356145368161412c565b9150606086013561454681614141565b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600060c082018883526020888185015287604085015260c0606085015281875180845260e086019150828901935060005b818110156145b75784518352938301939183019160010161459b565b5050809350505050836080830152600383106145d5576145d5614554565b8260a0830152979650505050505050565b803560ff81168114613f8457600080fd5b60008060008060008060008060006101008a8c03121561461657600080fd5b89356001600160401b0381111561462c57600080fd5b6146388c828d016140e1565b909a5098505060208a0135965060408a0135955060608a0135945060808a01356146618161412c565b935060a08a01356146718161412c565b925060c08a013561468181614141565b915061468f60e08b016145e6565b90509295985092959850929598565b600080604083850312156146b157600080fd5b50508035926020909101359150565b600080604083850312156146d357600080fd5b82356146de8161412c565b915060208301356146ee8161412c565b809150509250929050565b60008060008060008060a0878903121561471257600080fd5b86356001600160401b0381111561472857600080fd5b61473489828a016140e1565b90975095505060208701359350604087013561474f8161412c565b9250606087013561475f81614141565b915061476d608088016145e6565b90509295509295509295565b60008060006060848603121561478e57600080fd5b83356147998161412c565b925060208401356147a98161412c565b915060408401356147b98161412c565b809150509250925092565b600080602083850312156147d757600080fd5b82356001600160401b038111156147ed57600080fd5b6147f9858286016140e1565b90969095509350505050565b6000806000806080858703121561481b57600080fd5b84356148268161412c565b935060208501356148368161412c565b925060408501359150606085013561484d8161412c565b939692955090935050565b60038110610bb457600080fd5b6000806040838503121561487857600080fd5b82356148838161412c565b915060208301356146ee81614858565b600081518084526020808501945080840160005b838110156148c3578151875295820195908201906001016148a7565b509495945050505050565b602081526000612d056020830184614893565b634e487b7160e01b600052601160045260246000fd5b80820180821115612d0857612d086148e1565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b803561ffff81168114613f8457600080fd5b8035600281900b8114613f8457600080fd5b803562ffffff81168114613f8457600080fd5b600082601f8301126149af57600080fd5b813560206149bf614434836143f0565b82815260059290921b840181019181810190868411156149de57600080fd5b8286015b848110156144735780356001600160401b03811115614a015760008081fd5b8701603f81018913614a135760008081fd5b848101356040614a25614434836143f0565b8281526060928302840182019288820191908d851115614a455760008081fd5b948301945b84861015614aa25780868f031215614a625760008081fd5b614a6a614375565b614a7387614967565b8152614a808b88016145e6565b8b820152614a8f858801614979565b8186015283529485019491890191614a4a565b508752505050928401925083016149e2565b6000614ac2614434846143f0565b80848252602080830192508560051b850136811115614ae057600080fd5b855b81811015614c195780356001600160401b0380821115614b025760008081fd5b81890191506101608236031215614b195760008081fd5b614b2161439d565b82358152614b30868401614967565b868201526040614b41818501614967565b90820152606083810135908201526080614b5c8185016145e6565b9082015260a0614b6d848201614979565b9082015260c0614b7e84820161498b565b9082015260e08381013583811115614b965760008081fd5b614ba236828701614413565b8284015250506101008084013583811115614bbd5760008081fd5b614bc936828701614413565b828401525050610120614bdd8185016145e6565b908201526101408381013583811115614bf65760008081fd5b614c023682870161499e565b918301919091525087525050938201938201614ae2565b50919695505050505050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6001600160a01b0392831681529116602082015260400190565b600060208284031215614c8857600080fd5b5051919050565b6001600160a01b03831681526040810160048310614caf57614caf614554565b8260208301529392505050565b600060208284031215614cce57600080fd5b815161320081614141565b634e487b7160e01b600052603260045260246000fd5b600060208284031215614d0157600080fd5b81516132008161412c565b6001600160a01b03929092168252602082015260400190565b8082028115828204841417612d0857612d086148e1565b600082614d5957634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115612d0857612d086148e1565b600081518084526020808501945080840160005b838110156148c3578151805161ffff1688528381015160ff168489015260409081015160020b9088015260609096019590820190600101614d85565b600081518084526020808501808196508360051b8101915082860160005b85811015614e9357828403895281516101408151865286820151614e088888018261ffff169052565b5060408281015161ffff16908701526060808301519087015260808083015160ff169087015260a080830151600281900b82890152505060c08281015162ffffff169087015260e08083015160ff1690870152610100808301519087015261012091820151918601819052614e7f81870183614d71565b9a87019a9550505090840190600101614ddf565b5091979650505050505050565b6020815260008251610160806020850152614ebf610180850183614dc1565b91506020850151604085015260408501516060850152606085015160808501526080850151614ef960a08601826001600160a01b03169052565b5060a08501516001600160a01b03811660c08601525060c08501516001600160a01b03811660e08601525060e085015161010085810191909152850151610120614f468187018315159052565b8601519050610140614f5c8682018360ff169052565b90950151151593019290925250919050565b600081518084526020808501808196508360051b8101915082860160005b85811015614e93578284038952614fa4848351614d71565b98850198935090840190600101614f8c565b6000610160825184526020830151614fd4602086018261ffff169052565b506040830151614fea604086018261ffff169052565b50606083015160608501526080830151615009608086018260ff169052565b5060a083015161501e60a086018260020b9052565b5060c083015161503560c086018262ffffff169052565b5060e08301518160e086015261504d82860182614893565b91505061010080840151858303828701526150688382614893565b92505050610120808401516150818287018260ff169052565b5050610140808401518583038287015261405b8382614f6e565b600081518084526020808501808196508360051b8101915082860160005b85811015614e935782840389526150d1848351614fb6565b988501989350908401906001016150b9565b6060815260006150f6606083018661509b565b6001600160a01b0394851660208401529290931660409091015292915050565b60a08152600061512960a0830188614dc1565b6001600160a01b0396909616602083015250604081019390935260608301919091521515608090910152919050565b600181815b80851115615193578160001904821115615179576151796148e1565b8085161561518657918102915b93841c939080029061515d565b509250929050565b6000826151aa57506001612d08565b816151b757506000612d08565b81600181146151cd57600281146151d7576151f3565b6001915050612d08565b60ff8411156151e8576151e86148e1565b50506001821b612d08565b5060208310610133831016604e8410600b8410161715615216575081810a612d08565b6152208383615158565b8060001904821115615234576152346148e1565b029392505050565b6000612d05838361519b565b60408152600061525b6040830185614fb6565b90508260208301529392505050565b60006001820161527c5761527c6148e1565b5060010190565b608081526000615296608083018761509b565b60ff959095166020830152506040810192909252606090910152919050565b600080604083850312156152c857600080fd5b505080516020909101519092909150565b6080815260006152ec608083018761509b565b602083019590955250911515604083015260ff16606090910152919050565b6000806040838503121561531e57600080fd5b825161532981614858565b809250506020808401516001600160401b0381111561534757600080fd5b8401601f8101861361535857600080fd5b8051615366614434826143f0565b81815260059190911b8201830190838101908883111561538557600080fd5b928401925b828410156153ac57835161539d81614141565b8252928401929084019061538a565b80955050505050509250929050565b60c0815260006153ce60c083018961509b565b6020830197909752506040810194909452911515606084015260ff166080830152151560a090910152919050565b6000825160005b8181101561541d5760208186018101518583015201615403565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca2646970667358221220a8f4dc4e86456a9c4610cac1766197c10e940b365bc613ef0be3f0349a7cf11164736f6c63430008140033
Deployed Bytecode
0x60806040526004361061028c5760003560e01c806391b4ded91161015a578063c3b83f5f116100c1578063e6d1c8a61161007a578063e6d1c8a6146107ed578063ebc797721461080d578063f453ea7d14610822578063f5bf372e14610842578063f8a940b01461086f578063fd47e5ec1461088f57600080fd5b8063c3b83f5f1461072d578063c4d66de81461074d578063ce18a8371461076d578063d3dc75391461078d578063d43b3576146107ad578063e481a342146107cd57600080fd5b8063aeb0f16411610113578063aeb0f16414610657578063b4e7e5c614610677578063b6a3670414610697578063b8e15ed0146106b7578063c2c9e276146106ed578063c301086c1461070d57600080fd5b806391b4ded9146105ae57806393b0c2ae146105c4578063942b67dc146105d75780639a23be78146105f75780639a618c0f14610617578063ace81b711461063757600080fd5b806347842663116101fe5780636cfb6bf9116101b75780636cfb6bf9146104e75780636dbf6cc7146105075780637649226c1461052757806379ba5097146105595780638da5cb5b1461056e5780638ff9d5cf1461058e57600080fd5b80634784266314610410578063481c6a751461043057806348663e951461045057806350631ddc1461047057806353a47bb71461049d5780635c975abb146104bd57600080fd5b80631627540c116102505780631627540c1461034357806316c38b3c14610363578063177957a81461038357806329cf5368146103b957806338ea7103146103cc57806344cab7ee146103f057600080fd5b8063014e17b01461029857806304de96c5146102c15780630ca08207146102e15780630fe621901461030357806313af40351461032357600080fd5b3661029357005b600080fd5b6102ab6102a636600461414f565b6108bc565b6040516102b891906141e7565b60405180910390f35b3480156102cd57600080fd5b506013546102ab906001600160a01b031681565b3480156102ed57600080fd5b506103016102fc3660046141fb565b61096b565b005b34801561030f57600080fd5b506017546102ab906001600160a01b031681565b34801561032f57600080fd5b5061030161033e3660046141fb565b6109e6565b34801561034f57600080fd5b5061030161035e3660046141fb565b610af2565b34801561036f57600080fd5b5061030161037e366004614218565b610b45565b34801561038f57600080fd5b506102ab61039e3660046141fb565b6008602052600090815260409020546001600160a01b031681565b6102ab6103c7366004614235565b610bb7565b3480156103d857600080fd5b506103e260105481565b6040519081526020016102b8565b3480156103fc57600080fd5b506018546102ab906001600160a01b031681565b34801561041c57600080fd5b50600b546102ab906001600160a01b031681565b34801561043c57600080fd5b50600a546102ab906001600160a01b031681565b34801561045c57600080fd5b50600f546102ab906001600160a01b031681565b34801561047c57600080fd5b506103e261048b3660046142c4565b60066020526000908152604090205481565b3480156104a957600080fd5b506001546102ab906001600160a01b031681565b3480156104c957600080fd5b506003546104d79060ff1681565b60405190151581526020016102b8565b3480156104f357600080fd5b506103016105023660046142dd565b610cb1565b34801561051357600080fd5b5061030161052236600461447e565b610df3565b34801561053357600080fd5b506105476105423660046144e1565b610f14565b6040516102b89695949392919061456a565b34801561056557600080fd5b50610301610f52565b34801561057a57600080fd5b506000546102ab906001600160a01b031681565b34801561059a57600080fd5b50600c546102ab906001600160a01b031681565b3480156105ba57600080fd5b506103e260025481565b6102ab6105d23660046145f7565b61102a565b3480156105e357600080fd5b506103016105f236600461469e565b611108565b34801561060357600080fd5b506103016106123660046146c0565b6111c0565b34801561062357600080fd5b506011546102ab906001600160a01b031681565b34801561064357600080fd5b506105476106523660046146f9565b61122a565b34801561066357600080fd5b506007546102ab906001600160a01b031681565b34801561068357600080fd5b50610301610692366004614779565b611269565b3480156106a357600080fd5b50600e546102ab906001600160a01b031681565b3480156106c357600080fd5b506102ab6106d23660046141fb565b6016602052600090815260409020546001600160a01b031681565b3480156106f957600080fd5b506103016107083660046147c4565b6112f5565b34801561071957600080fd5b506103016107283660046141fb565b611497565b34801561073957600080fd5b506103016107483660046141fb565b6114ea565b34801561075957600080fd5b506103016107683660046141fb565b6115da565b34801561077957600080fd5b506014546102ab906001600160a01b031681565b34801561079957600080fd5b50600d546102ab906001600160a01b031681565b3480156107b957600080fd5b506103016107c8366004614805565b6116e4565b3480156107d957600080fd5b506102ab6107e8366004614235565b6117ee565b3480156107f957600080fd5b506103016108083660046146c0565b6118c1565b34801561081957600080fd5b50610301611a4e565b34801561082e57600080fd5b5061030161083d3660046142c4565b611aac565b34801561084e57600080fd5b506103e261085d3660046141fb565b60156020526000908152604090205481565b34801561087b57600080fd5b5061030161088a366004614865565b611ae9565b34801561089b57600080fd5b506108af6108aa3660046147c4565b611cc1565b6040516102b891906148ce565b60006001600460008282546108d191906148f7565b909155505060045460035460ff16156109055760405162461bcd60e51b81526004016108fc9061490a565b60405180910390fd5b61093b6109128a8c614ab4565b60408051606081018252600080825260208201819052918101919091528a8a8a8a8a8a33611d6c565b9150600454811461095e5760405162461bcd60e51b81526004016108fc90614c25565b5098975050505050505050565b610973611e4b565b600754601154610990916001600160a01b03908116911683611ebf565b601180546001600160a01b0319166001600160a01b0383161790556040517f7d16d7e66eb2263f48c2c24af20e095395796ce3d05df64d84d9991a4d436247906109db9083906141e7565b60405180910390a150565b6001600160a01b038116610a385760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064016108fc565b600154600160a01b900460ff1615610aa45760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b60648201526084016108fc565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b031990911617815560405160008051602061542c833981519152916109db918490614c5c565b610afa611e4b565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906109db9083906141e7565b610b4d611e4b565b60035460ff16151581151514610bb4576003805460ff191682151590811790915560ff1615610b7b57426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec5906020016109db565b50565b6000600160046000828254610bcc91906148f7565b909155505060045460035460ff1615610bf75760405162461bcd60e51b81526004016108fc9061490a565b846001600160a01b038116610c1f576040516328f6040160e21b815260040160405180910390fd5b6018546001600160a01b03163314610c4a57604051637bcec4fb60e01b815260040160405180910390fd5b610c81610c578a8c614ab4565b60408051606081018252600080825260016020830152918101829052908b908b908a8a828e611d6c565b9250506004548114610ca55760405162461bcd60e51b81526004016108fc90614c25565b50979650505050505050565b610cb9611e4b565b600780546001600160a01b0319166001600160a01b0388169081179091556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa158015610d12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d369190614c76565b600955600a80546001600160a01b038781166001600160a01b03199283168117909355600b80548883169084168117909155600c80548884169085168117909155600d80548885169086168117909155600f805488861696168617905560408051948d1685526020850196909652948301919091526060820152608081019290925260a08201527f1962de5c532b26a8a72c8524fe745d05421f16a6521fc531ce010572f742a6939060c0015b60405180910390a1505050505050565b600a5460405163e760c39560e01b81526001600160a01b039091169063e760c39590610e26903390600090600401614c8f565b602060405180830381865afa158015610e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e679190614cbc565b158015610e7f57506000546001600160a01b03163314155b15610e9d57604051636edaef2f60e11b815260040160405180910390fd5b8051825114610ebf5760405163251f56a160e21b815260040160405180910390fd5b60005b8251811015610f0f57610f07838281518110610ee057610ee0614cd9565b6020026020010151838381518110610efa57610efa614cd9565b6020026020010151611fca565b600101610ec2565b505050565b60008060006060600080610f2d8b8b8b8b8b6000612019565b809650819750829850839950849a50859b505050505050509550955095509550955095565b6001546001600160a01b03163314610fca5760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b60648201526084016108fc565b60005460015460405160008051602061542c83398151915292610ffb926001600160a01b0391821692911690614c5c565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b600060016004600082825461103f91906148f7565b909155505060045460035460ff161561106a5760405162461bcd60e51b81526004016108fc9061490a565b60018360ff16111580611080575060ff83168a11155b1561109e57604051631a0a722b60e01b815260040160405180910390fd5b6110d76110ab8b8d614ab4565b6040805160608101825260ff87168152600060208201819052918101919091528b8b8b8b8b8b33611d6c565b915060045481146110fa5760405162461bcd60e51b81526004016108fc90614c25565b509998505050505050505050565b600a5460405163e760c39560e01b81526001600160a01b039091169063e760c3959061113b903390600090600401614c8f565b602060405180830381865afa158015611158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117c9190614cbc565b15801561119457506000546001600160a01b03163314155b156111b257604051636edaef2f60e11b815260040160405180910390fd5b6111bc8282611fca565b5050565b6111c8611e4b565b604051634946980760e11b81526001600160a01b0383169063928d300e906111f49084906004016141e7565b600060405180830381600087803b15801561120e57600080fd5b505af1158015611222573d6000803e3d6000fd5b505050505050565b600080600060606000806112428c8c8c8c8c8c612019565b809650819750829850839950849a50859b5050505050505096509650965096509650969050565b611271611e4b565b601380546001600160a01b038581166001600160a01b0319928316811790935560188054868316908416811790915560148054928616929093168217909255604080519384526020840192909252908201527f2b8bc92a77df3bf6abc793bbc13c813f4e0028dd6de7aefb204a96ade4ddc4739060600160405180910390a1505050565b6112fd611e4b565b60005b81811015610f0f57600083838381811061131c5761131c614cd9565b905060200201602081019061133191906141fb565b90506000816001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611372573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113969190614cef565b6014549091506001600160a01b038083169116148015906113c557506017546001600160a01b03828116911614155b1561148d5760405163646d919f60e11b81526001600160a01b0383169063c8db233e906113f69033906004016141e7565b600060405180830381600087803b15801561141057600080fd5b505af1158015611424573d6000803e3d6000fd5b5050600a5460405163f033d0f960e01b81526001600160a01b03909116925063f033d0f9915061145a9085908590600401614c5c565b600060405180830381600087803b15801561147457600080fd5b505af1158015611488573d6000803e3d6000fd5b505050505b5050600101611300565b61149f611e4b565b600e80546001600160a01b0319166001600160a01b0383161790556040517fab496e3942a33c5a729be5985af46209e18077a462af550b750246460950eb7f906109db9083906141e7565b6114f2611e4b565b6001600160a01b03811661153a5760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016108fc565b600154600160a81b900460ff161561158a5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b60448201526064016108fc565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b17905560405160008051602061542c833981519152916109db918490614c5c565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b031660008115801561161f5750825b90506000826001600160401b0316600114801561163b5750303b155b905081158015611649575080155b156116675760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561169157845460ff60401b1916600160401b1785555b61169a866109e6565b6116a2611a4e565b831561122257845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610de3565b6116ec611e4b565b6001600160a01b03848116600090815260086020526040902054811690841614611766576001600160a01b038085166000908152600860205260409020546117379186911685611ebf565b6001600160a01b03848116600090815260086020526040902080546001600160a01b0319169185169190911790555b6001600160a01b038481166000818152601560209081526040808320879055601682529182902080546001600160a01b031916868616908117909155825193845293871690830152810184905260608101919091527f2083b2a6d9c12b8cd65a8a4d92a82708aac25a81a524e9d13e92559bf818db569060800160405180910390a150505050565b600060016004600082825461180391906148f7565b909155505060045460035460ff161561182e5760405162461bcd60e51b81526004016108fc9061490a565b846001600160a01b038116611856576040516328f6040160e21b815260040160405180910390fd5b6013546001600160a01b0316331461188157604051637bcec4fb60e01b815260040160405180910390fd5b610c8161188e8a8c614ab4565b6040518060600160405280600060ff168152602001600015158152602001600115158152508a8a60008a8a60008e611d6c565b6001600460008282546118d491906148f7565b909155505060045460035460ff16156118ff5760405162461bcd60e51b81526004016108fc9061490a565b600a546040516321875b5760e01b815284916001600160a01b0316906321875b579061192f9084906004016141e7565b602060405180830381865afa15801561194c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119709190614cbc565b61198d57604051634f8c20e160e01b815260040160405180910390fd5b836001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ee9190614cef565b6001600160a01b0316336001600160a01b031614611a1f5760405163bba02e7160e01b815260040160405180910390fd5b611a2c8484600080612264565b506004548114610f0f5760405162461bcd60e51b81526004016108fc90614c25565b60055460ff1615611a975760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016108fc565b6005805460ff19166001908117909155600455565b611ab4611e4b565b60108190556040518181527f5da87bc83066ddf563cfd9de25e562860f147b358751f50766847996202e5d33906020016109db565b600160046000828254611afc91906148f7565b909155505060045460035460ff1615611b275760405162461bcd60e51b81526004016108fc9061490a565b600a546040516321875b5760e01b815284916001600160a01b0316906321875b5790611b579084906004016141e7565b602060405180830381865afa158015611b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b989190614cbc565b611bb557604051634f8c20e160e01b815260040160405180910390fd5b6000836002811115611bc957611bc9614554565b14611c5f57600a5460405163e760c39560e01b81526001600160a01b039091169063e760c39590611c01903390600290600401614c8f565b602060405180830381865afa158015611c1e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c429190614cbc565b611c5f57604051637830359760e01b815260040160405180910390fd5b6001836002811115611c7357611c73614554565b03611c8c57611c8784600060016000612264565b611a2c565b6002836002811115611ca057611ca0614554565b03611cb357611c87846000806001612264565b611a2c846000806000612264565b606081806001600160401b03811115611cdc57611cdc61435f565b604051908082528060200260200182016040528015611d05578160200160208202803683370190505b50915060005b81811015611d645760066000868684818110611d2957611d29614cd9565b90506020020135815260200190815260200160002054838281518110611d5157611d51614cd9565b6020908102919091010152600101611d0b565b505092915050565b6000861580611d79575087155b15611d975760405163d1892e7760e01b815260040160405180910390fd5b611da185836128a7565b600080611db08a8786886128e8565b604080516101208101909152828152919d5098509193509150611e3b908d9060208101611ddd8e8e612ce6565b81526020018b8152602001876001600160a01b031681526020018e6040015115158152602001896001600160a01b03168152602001856001600160a01b031681526020018481526020018e6020015115158152508d60000151612d0e565b9c9b505050505050505050505050565b6000546001600160a01b03163314611ebd5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b60648201526084016108fc565b565b6001600160a01b03821615611f425760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390611efd908590600090600401614d0c565b6020604051808303816000875af1158015611f1c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f409190614cbc565b505b6001600160a01b03811615610f0f5760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390611f8190849060001990600401614d0c565b6020604051808303816000875af1158015611fa0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc49190614cbc565b50505050565b60008281526006602090815260409182902083905581518481529081018390527fc6fa3d673d901ef180e5a314ff8ede38ac8ba226ce71c9d822ed8a438020a1ab910160405180910390a15050565b6000808060608782816001600160a01b038a161580159061204857506007546001600160a01b038b8116911614155b156121cd576001600160a01b03808b1660009081526008602052604090205416806120fb57601160009054906101000a90046001600160a01b03166001600160a01b031663cb4ba5248c8e6040518363ffffffff1660e01b81526004016120b0929190614d0c565b602060405180830381865afa1580156120cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f19190614c76565b93508391506121cb565b6121c88c826001600160a01b031663f7683bbc6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561213d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121619190614c76565b8d6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561219f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121c39190614c76565b613207565b93505b505b806000036121ee57604051631f2a200560e01b815260040160405180910390fd5b6122406121fb8d8f614ab4565b6040805160e081018252848152600160208201529081018690526001600160a01b038d1660608201528b15156080820152600060a0820181905260c08201528a61327d565b809650819850829950839a50849b5050505050505096509650965096509650969050565b83600082156122d857816001600160a01b0316636a265aa96040518163ffffffff1660e01b81526004016020604051808303816000875af11580156122ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d19190614c76565b9050612390565b831561231e57816001600160a01b031663ea8a1af06040518163ffffffff1660e01b81526004016020604051808303816000875af11580156122ad573d6000803e3d6000fd5b604051630d8acc1560e11b81526001600160a01b03831690631b15982a9061234a9088906004016141e7565b6020604051808303816000875af1158015612369573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061238d9190614c76565b90505b6000826001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa1580156123d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123f49190614cef565b90506000836001600160a01b031662641e8b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612435573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124599190614cef565b6014549091506001600160a01b038083169116148061248557506017546001600160a01b038281169116145b156124e95760405163509eb5b560e11b81526001600160a01b0382169063a13d6b6a906124b6908b906004016141e7565b600060405180830381600087803b1580156124d057600080fd5b505af11580156124e4573d6000803e3d6000fd5b505050505b836001600160a01b0316639a82a09a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612527573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061254b9190614cbc565b6125bd576125bb846001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa158015612590573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125b49190614c76565b82846137df565b505b600a546040516306ef036760e01b81526001600160a01b03909116906306ef0367906125ef908b908590600401614c5c565b600060405180830381600087803b15801561260957600080fd5b505af115801561261d573d6000803e3d6000fd5b505050507f1a208f07d825103c5b1eb3bb1109b420fd544123300beb9e220fc951cbd7dd0b8882866001600160a01b0316633356a35a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612682573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126a69190614cbc565b604080516001600160a01b03948516815293909216602084015215159082015260600160405180910390a16000831180156126e957506001600160a01b03871615155b80156127075750816001600160a01b0316876001600160a01b031614155b156127c6576007546001600160a01b0383811691161461273a5760405163c6338d5560e01b815260040160405180910390fd5b601154604051630992646d60e31b81526127c69183916001600160a01b0390911690634c93236890612772908c908990600401614d0c565b6020604051808303816000875af1158015612791573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b59190614c76565b6001600160a01b038a169190613aa7565b6001600160a01b03808316600081815260086020526040908190205490516370a0823160e01b815292169163d7efa129918b916370a082319061280d9030906004016141e7565b602060405180830381865afa15801561282a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284e9190614c76565b6040518363ffffffff1660e01b815260040161286b929190614d0c565b600060405180830381600087803b15801561288557600080fd5b505af1158015612899573d6000803e3d6000fd5b505050505050505050505050565b6001600160a01b038216156111bc57600d5460405163bbddaca360e01b81526001600160a01b039091169063bbddaca3906111f49085908590600401614c5c565b60008085816001600160a01b038716158061291057506007546001600160a01b038881169116145b1561293357506007546001600160a01b031661292e8187308b613aff565b612cbd565b50858415612a4857601160009054906101000a90046001600160a01b03166001600160a01b0316634aa4a4fc6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561298e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129b29190614cef565b6001600160a01b0316876001600160a01b03161415806129d157508734105b156129ef57604051634f4d1d8760e01b815260040160405180910390fd5b866001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b158015612a2a57600080fd5b505af1158015612a3e573d6000803e3d6000fd5b5050505050612a5d565b612a5d6001600160a01b03881687308b613aff565b6001600160a01b038088166000908152600860205260409020541693508315612b0557836001600160a01b031663f7683bbc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612abe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ae29190614c76565b92508260000361292e5760405163577f86e160e11b815260040160405180910390fd5b6011546001600160a01b0316612b2e57604051630d5f109960e21b815260040160405180910390fd5b60115460405163095ea7b360e01b81526001600160a01b038981169263095ea7b392612b6292909116908c90600401614d0c565b6020604051808303816000875af1158015612b81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba59190614cbc565b506011546040516322ceb11360e21b81526001600160a01b0390911690638b3ac44c90612bd8908a908c90600401614d0c565b6020604051808303816000875af1158015612bf7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c1b9190614c76565b6011546040516332d2e94960e21b81529193506001600160a01b03169063cb4ba52490612c4e908a908c90600401614d0c565b602060405180830381865afa158015612c6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c8f9190614c76565b821015612caf5760405163a3b7d2e560e01b815260040160405180910390fd5b506007546001600160a01b03165b6001600160a01b0380821660009081526008602052604090205416989297509095509350915050565b600081612cfb84670de0b6b3a7640000614d25565b612d059190614d3c565b90505b92915050565b6000612d426040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60a08401516001600160a01b031660009081526015602052604090205460608201526080840151612def57612ddc856040518060e0016040528087600001518152602001600015158152602001600081526020018760a001516001600160a01b0316815260200187608001511515815260200187610100015115158152602001612dd488600001518960200151612ce6565b90528561327d565b5050604084015260208301528152612e76565b612e0184600001518560200151612ce6565b8082526060820151612e1291613b38565b6060820151612e2990670de0b6b3a76400006148f7565b612e339190614d5e565b8151612e4890670de0b6b3a764000090614d25565b612e529190614d3c565b8082528451612e6091612ce6565b60208201528351612e7090613b4d565b60408201525b80604001518160200151612e8a91906148f7565b608082015280516020820151612ea39187918787613b70565b6000612eb3868360600151613d0f565b600e54909150600090612ece906001600160a01b0316613f17565b9050806001600160a01b031663b5ac8d98604051806101600160405280858152602001896000015181526020018660400151815260200186600001518152602001306001600160a01b0316815260200189606001516001600160a01b031681526020018960a001516001600160a01b03168152602001600b60009054906101000a90046001600160a01b03166001600160a01b031663b3ee14246040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fbb9190614c76565b612fc590426148f7565b81526020018960800151151581526020018860ff16815260200189610100015115158152506040518263ffffffff1660e01b81526004016130069190614ea0565b600060405180830381600087803b15801561302057600080fd5b505af1158015613034573d6000803e3d6000fd5b5050600a54606089015160405163514dd41d60e11b81526001600160a01b03909216935063a29ba83a925061306f918b9186916004016150e3565b600060405180830381600087803b15801561308957600080fd5b505af115801561309d573d6000803e3d6000fd5b505050508560c001516001600160a01b03166358c09cc082886000015186608001516130c99190614d5e565b6040518363ffffffff1660e01b81526004016130e6929190614d0c565b600060405180830381600087803b15801561310057600080fd5b505af1158015613114573d6000803e3d6000fd5b505050608084015160a088015161313792506001600160a01b0316908390613aa7565b8551602084015160808801516040517fece8b280c05fcdb1e6f073f39266f576f9ee1f91bfe098dbb5f7b5e3dce305ba936131759387938793615116565b60405180910390a16060868101518751604086810151602080890151895160a0808f015186516001600160a01b038c81168252998a16958101959095528487019790975297830193909352608082015294850152911660c0830152517fddd6ef4a731d70ec093d7f6a82160e586a40dde73b44129dcb771a5b64557d8a9181900360e00190a1925050505b9392505050565b60006132138484613b38565b9050600954821015613249578160095461322d9190614d5e565b61323890600a61523c565b6132429082614d25565b9050613200565b600954821115613200576009546132609083614d5e565b61326b90600a61523c565b6132759082614d3c565b949350505050565b6000806000606060008088519050806001600160401b038111156132a3576132a361435f565b6040519080825280602002602001820160405280156132cc578160200160208202803683370190505b5092506000600b60009054906101000a90046001600160a01b03166001600160a01b031663e88698bf6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613324573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133489190614c76565b60608a01516001600160a01b0316600090815260156020526040812054919250600160ff8b161191905b848110156135855760008d828151811061338e5761338e614cd9565b602090810291909101810151600b548151600090815260069093526040928390205492516311b3db5760e11b81529193506001600160a01b031691632367b6ae916133dd918591600401615248565b60006040518083038186803b1580156133f557600080fd5b505afa158015613409573d6000803e3d6000fd5b5050505080610120015160ff168160e00151511161343a5760405163673f032f60e11b815260040160405180910390fd5b60008160e0015182610120015160ff168151811061345a5761345a614cd9565b6020026020010151905061346e8482613b38565b61348085670de0b6b3a76400006148f7565b61348a9190614d5e565b61349c670de0b6b3a764000083614d25565b6134a69190614d3c565b8e519091506134b58183612ce6565b6134bf9190614d5e565b8984815181106134d1576134d1614cd9565b6020026020010181815250508415613559576134f5670de0b6b3a764000088614d25565b8d60ff16670de0b6b3a76400008b868151811061351457613514614cd9565b60200260200101516135269190614d25565b6135309190614d25565b61353a9190614d3c565b89848151811061354c5761354c614cd9565b6020026020010181815250505b8b1561356e576135698c82613b38565b613570565b805b9b5050508061357e9061526a565b9050613372565b5088156137d057811561362657600b60009054906101000a90046001600160a01b03166001600160a01b031663ff527ee38d8c8e60000151856040518563ffffffff1660e01b81526004016135dd9493929190615283565b6040805180830381865afa1580156135f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061361d91906152b5565b9950975061368b565b8a60a001511561367c578a60c001519850613641818a613b38565b61365382670de0b6b3a76400006148f7565b61365d9190614d5e565b61366f670de0b6b3a76400008b614d25565b6136799190614d3c565b98505b8a51613688908a612ce6565b97505b828910156136a7578298506136a48b600001518a612ce6565b97505b8a516136b290613b4d565b96508a60200151156137d0576060600b60009054906101000a90046001600160a01b03166001600160a01b031663c360aded8e8e604001518f608001518f6040518563ffffffff1660e01b815260040161370f94939291906152d9565b600060405180830381865afa15801561372c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613754919081019061530b565b909650905060005b858110156137ac5781818151811061377657613776614cd9565b6020026020010151156137a457600088828151811061379757613797614cd9565b6020026020010181815250505b60010161375c565b5060008660028111156137c1576137c1614554565b146137ce57600098508899505b505b50505050939792965093509350565b600d54604051630293b59d60e31b8152600091829182916001600160a01b03169063149dace8906138149088906004016141e7565b602060405180830381865afa158015613831573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138559190614cef565b90506000846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161388591906141e7565b602060405180830381865afa1580156138a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138c69190614c76565b90506001600160a01b038216156139e057600d5460405163c7d1f5f160e01b81526000916001600160a01b03169063c7d1f5f1906139089086906004016141e7565b602060405180830381865afa158015613925573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139499190614c76565b905080156139de5761395b8882613b38565b93508382106139de576139786001600160a01b0387168486613aa7565b604080516001600160a01b0385811682528981166020830152818301879052606082018b90528816608082015290517f2f60e9c3fac363f0b03efb55d381f3d47475dbf1230e99201c9cdf4b9f577fc79181900360a00190a16139db8483614d5e565b91505b505b6139e987613b4d565b935082841115613a9d5760006139ff8486614d5e565b9050808210613a9b576001600160a01b0380871660009081526016602052604090205416613a4f81613a3c57600f546001600160a01b0316613a3e565b815b6001600160a01b0389169084613aa7565b60105460408051918252602082018490526001600160a01b03891682820152517f05b8bddc7eed21b7839baa291868ed966dd7299b79495dadd406c782b9770fe79181900360600190a1505b505b5050509392505050565b610f0f83846001600160a01b031663a9059cbb8585604051602401613acd929190614d0c565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613f89565b6040516001600160a01b038481166024830152838116604483015260648201839052611fc49186918216906323b872dd90608401613acd565b6000670de0b6b3a7640000612cfb8385614d25565b6000670de0b6b3a764000060105483613b669190614d25565b612d089190614d3c565b815160e083015160208401518115613c155760008560a001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613bc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bea9190614c76565b9050613bf7848483613207565b9350613c04878483613207565b9650613c11828483613207565b9150505b600b5460808601516101008701516040516354c0545160e01b81526001600160a01b03909316926354c0545192613c59928d9289928d9290918c91906004016153bb565b600060405180830381600087803b158015613c7357600080fd5b505af1158015613c87573d6000803e3d6000fd5b5050600b546040888101518c5191516318542d5360e31b815260048101899052602481018d9052604481018c905260648101879052608481019190915260a48101919091526001600160a01b03909116925063c2a16a98915060c40160006040518083038186803b158015613cfb57600080fd5b505afa158015612899573d6000803e3d6000fd5b8151606090806001600160401b03811115613d2c57613d2c61435f565b604051908082528060200260200182016040528015613dad57816020015b60408051610140810182526000808252602080830182905292820181905260608083018290526080830182905260a0830182905260c0830182905260e083018290526101008301919091526101208201528252600019909201910181613d4a5790505b50915060005b81811015611d64576000858281518110613dcf57613dcf614cd9565b6020026020010151905060008160e0015182610120015160ff1681518110613df957613df9614cd9565b6020026020010151905060405180610140016040528083600001518152602001836020015161ffff168152602001836040015161ffff16815260200183606001518152602001836080015160ff1681526020018360a0015160020b81526020018360c0015162ffffff16815260200183610120015160ff168152602001613e808884613b38565b613e9289670de0b6b3a76400006148f7565b613e9c9190614d5e565b613eae670de0b6b3a764000085614d25565b613eb89190614d3c565b815260200183610140015184610120015160ff1681518110613edc57613edc614cd9565b6020026020010151815250858481518110613ef957613ef9614cd9565b6020026020010181905250505080613f109061526a565b9050613db3565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c176000526e5af43d82803e903d91602b57fd5bf38260781b17602052603760096000f090506001600160a01b038116613f84576040516330be1a3d60e21b815260040160405180910390fd5b919050565b6000613f9e6001600160a01b03841683613fe3565b90508051600014158015613fc3575080806020019051810190613fc19190614cbc565b155b15610f0f5782604051635274afe760e01b81526004016108fc91906141e7565b6060612d058383600084600080856001600160a01b0316848660405161400991906153fc565b60006040518083038185875af1925050503d8060008114614046576040519150601f19603f3d011682016040523d82523d6000602084013e61404b565b606091505b509150915061405b868383614065565b9695505050505050565b60608261407a57614075826140b8565b613200565b815115801561409157506001600160a01b0384163b155b156140b15783604051639996b31560e01b81526004016108fc91906141e7565b5080613200565b8051156140c85780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60008083601f8401126140f357600080fd5b5081356001600160401b0381111561410a57600080fd5b6020830191508360208260051b850101111561412557600080fd5b9250929050565b6001600160a01b0381168114610bb457600080fd5b8015158114610bb457600080fd5b60008060008060008060008060e0898b03121561416b57600080fd5b88356001600160401b0381111561418157600080fd5b61418d8b828c016140e1565b90995097505060208901359550604089013594506060890135935060808901356141b68161412c565b925060a08901356141c68161412c565b915060c08901356141d681614141565b809150509295985092959890939650565b6001600160a01b0391909116815260200190565b60006020828403121561420d57600080fd5b81356132008161412c565b60006020828403121561422a57600080fd5b813561320081614141565b600080600080600080600060c0888a03121561425057600080fd5b87356001600160401b0381111561426657600080fd5b6142728a828b016140e1565b909850965050602088013594506040880135935060608801356142948161412c565b925060808801356142a48161412c565b915060a08801356142b48161412c565b8091505092959891949750929550565b6000602082840312156142d657600080fd5b5035919050565b60008060008060008060c087890312156142f657600080fd5b86356143018161412c565b955060208701356143118161412c565b945060408701356143218161412c565b935060608701356143318161412c565b925060808701356143418161412c565b915060a08701356143518161412c565b809150509295509295509295565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b03811182821017156143975761439761435f565b60405290565b60405161016081016001600160401b03811182821017156143975761439761435f565b604051601f8201601f191681016001600160401b03811182821017156143e8576143e861435f565b604052919050565b60006001600160401b038211156144095761440961435f565b5060051b60200190565b600082601f83011261442457600080fd5b81356020614439614434836143f0565b6143c0565b82815260059290921b8401810191818101908684111561445857600080fd5b8286015b84811015614473578035835291830191830161445c565b509695505050505050565b6000806040838503121561449157600080fd5b82356001600160401b03808211156144a857600080fd5b6144b486838701614413565b935060208501359150808211156144ca57600080fd5b506144d785828601614413565b9150509250929050565b6000806000806000608086880312156144f957600080fd5b85356001600160401b0381111561450f57600080fd5b61451b888289016140e1565b9096509450506020860135925060408601356145368161412c565b9150606086013561454681614141565b809150509295509295909350565b634e487b7160e01b600052602160045260246000fd5b600060c082018883526020888185015287604085015260c0606085015281875180845260e086019150828901935060005b818110156145b75784518352938301939183019160010161459b565b5050809350505050836080830152600383106145d5576145d5614554565b8260a0830152979650505050505050565b803560ff81168114613f8457600080fd5b60008060008060008060008060006101008a8c03121561461657600080fd5b89356001600160401b0381111561462c57600080fd5b6146388c828d016140e1565b909a5098505060208a0135965060408a0135955060608a0135945060808a01356146618161412c565b935060a08a01356146718161412c565b925060c08a013561468181614141565b915061468f60e08b016145e6565b90509295985092959850929598565b600080604083850312156146b157600080fd5b50508035926020909101359150565b600080604083850312156146d357600080fd5b82356146de8161412c565b915060208301356146ee8161412c565b809150509250929050565b60008060008060008060a0878903121561471257600080fd5b86356001600160401b0381111561472857600080fd5b61473489828a016140e1565b90975095505060208701359350604087013561474f8161412c565b9250606087013561475f81614141565b915061476d608088016145e6565b90509295509295509295565b60008060006060848603121561478e57600080fd5b83356147998161412c565b925060208401356147a98161412c565b915060408401356147b98161412c565b809150509250925092565b600080602083850312156147d757600080fd5b82356001600160401b038111156147ed57600080fd5b6147f9858286016140e1565b90969095509350505050565b6000806000806080858703121561481b57600080fd5b84356148268161412c565b935060208501356148368161412c565b925060408501359150606085013561484d8161412c565b939692955090935050565b60038110610bb457600080fd5b6000806040838503121561487857600080fd5b82356148838161412c565b915060208301356146ee81614858565b600081518084526020808501945080840160005b838110156148c3578151875295820195908201906001016148a7565b509495945050505050565b602081526000612d056020830184614893565b634e487b7160e01b600052601160045260246000fd5b80820180821115612d0857612d086148e1565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b803561ffff81168114613f8457600080fd5b8035600281900b8114613f8457600080fd5b803562ffffff81168114613f8457600080fd5b600082601f8301126149af57600080fd5b813560206149bf614434836143f0565b82815260059290921b840181019181810190868411156149de57600080fd5b8286015b848110156144735780356001600160401b03811115614a015760008081fd5b8701603f81018913614a135760008081fd5b848101356040614a25614434836143f0565b8281526060928302840182019288820191908d851115614a455760008081fd5b948301945b84861015614aa25780868f031215614a625760008081fd5b614a6a614375565b614a7387614967565b8152614a808b88016145e6565b8b820152614a8f858801614979565b8186015283529485019491890191614a4a565b508752505050928401925083016149e2565b6000614ac2614434846143f0565b80848252602080830192508560051b850136811115614ae057600080fd5b855b81811015614c195780356001600160401b0380821115614b025760008081fd5b81890191506101608236031215614b195760008081fd5b614b2161439d565b82358152614b30868401614967565b868201526040614b41818501614967565b90820152606083810135908201526080614b5c8185016145e6565b9082015260a0614b6d848201614979565b9082015260c0614b7e84820161498b565b9082015260e08381013583811115614b965760008081fd5b614ba236828701614413565b8284015250506101008084013583811115614bbd5760008081fd5b614bc936828701614413565b828401525050610120614bdd8185016145e6565b908201526101408381013583811115614bf65760008081fd5b614c023682870161499e565b918301919091525087525050938201938201614ae2565b50919695505050505050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6001600160a01b0392831681529116602082015260400190565b600060208284031215614c8857600080fd5b5051919050565b6001600160a01b03831681526040810160048310614caf57614caf614554565b8260208301529392505050565b600060208284031215614cce57600080fd5b815161320081614141565b634e487b7160e01b600052603260045260246000fd5b600060208284031215614d0157600080fd5b81516132008161412c565b6001600160a01b03929092168252602082015260400190565b8082028115828204841417612d0857612d086148e1565b600082614d5957634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115612d0857612d086148e1565b600081518084526020808501945080840160005b838110156148c3578151805161ffff1688528381015160ff168489015260409081015160020b9088015260609096019590820190600101614d85565b600081518084526020808501808196508360051b8101915082860160005b85811015614e9357828403895281516101408151865286820151614e088888018261ffff169052565b5060408281015161ffff16908701526060808301519087015260808083015160ff169087015260a080830151600281900b82890152505060c08281015162ffffff169087015260e08083015160ff1690870152610100808301519087015261012091820151918601819052614e7f81870183614d71565b9a87019a9550505090840190600101614ddf565b5091979650505050505050565b6020815260008251610160806020850152614ebf610180850183614dc1565b91506020850151604085015260408501516060850152606085015160808501526080850151614ef960a08601826001600160a01b03169052565b5060a08501516001600160a01b03811660c08601525060c08501516001600160a01b03811660e08601525060e085015161010085810191909152850151610120614f468187018315159052565b8601519050610140614f5c8682018360ff169052565b90950151151593019290925250919050565b600081518084526020808501808196508360051b8101915082860160005b85811015614e93578284038952614fa4848351614d71565b98850198935090840190600101614f8c565b6000610160825184526020830151614fd4602086018261ffff169052565b506040830151614fea604086018261ffff169052565b50606083015160608501526080830151615009608086018260ff169052565b5060a083015161501e60a086018260020b9052565b5060c083015161503560c086018262ffffff169052565b5060e08301518160e086015261504d82860182614893565b91505061010080840151858303828701526150688382614893565b92505050610120808401516150818287018260ff169052565b5050610140808401518583038287015261405b8382614f6e565b600081518084526020808501808196508360051b8101915082860160005b85811015614e935782840389526150d1848351614fb6565b988501989350908401906001016150b9565b6060815260006150f6606083018661509b565b6001600160a01b0394851660208401529290931660409091015292915050565b60a08152600061512960a0830188614dc1565b6001600160a01b0396909616602083015250604081019390935260608301919091521515608090910152919050565b600181815b80851115615193578160001904821115615179576151796148e1565b8085161561518657918102915b93841c939080029061515d565b509250929050565b6000826151aa57506001612d08565b816151b757506000612d08565b81600181146151cd57600281146151d7576151f3565b6001915050612d08565b60ff8411156151e8576151e86148e1565b50506001821b612d08565b5060208310610133831016604e8410600b8410161715615216575081810a612d08565b6152208383615158565b8060001904821115615234576152346148e1565b029392505050565b6000612d05838361519b565b60408152600061525b6040830185614fb6565b90508260208301529392505050565b60006001820161527c5761527c6148e1565b5060010190565b608081526000615296608083018761509b565b60ff959095166020830152506040810192909252606090910152919050565b600080604083850312156152c857600080fd5b505080516020909101519092909150565b6080815260006152ec608083018761509b565b602083019590955250911515604083015260ff16606090910152919050565b6000806040838503121561531e57600080fd5b825161532981614858565b809250506020808401516001600160401b0381111561534757600080fd5b8401601f8101861361535857600080fd5b8051615366614434826143f0565b81815260059190911b8201830190838101908883111561538557600080fd5b928401925b828410156153ac57835161539d81614141565b8252928401929084019061538a565b80955050505050509250929050565b60c0815260006153ce60c083018961509b565b6020830197909752506040810194909452911515606084015260ff166080830152151560a090910152919050565b6000825160005b8181101561541d5760208186018101518583015201615403565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca2646970667358221220a8f4dc4e86456a9c4610cac1766197c10e940b365bc613ef0be3f0349a7cf11164736f6c63430008140033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 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.