Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00
Cross-Chain Transactions
Loading...
Loading
Contract Name:
FreeBetsHolder
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 "@thales-dao/contracts/contracts/interfaces/IAddressManager.sol";
import "../../interfaces/ISpeedMarketsAMMCreator.sol";
import "../../interfaces/ISpeedMarketsAMM.sol";
import "../../utils/proxy/ProxyOwned.sol";
import "../../utils/proxy/ProxyPausable.sol";
import "../../utils/proxy/ProxyReentrancyGuard.sol";
import "../../utils/libraries/AddressSetLib.sol";
import "../../interfaces/ISportsAMMV2.sol";
import "../../interfaces/ILiveTradingProcessor.sol";
import "../../interfaces/ISGPTradingProcessor.sol";
import "./../AMM/Ticket.sol";
contract FreeBetsHolder is Initializable, ProxyOwned, ProxyPausable, ProxyReentrancyGuard {
using SafeERC20 for IERC20;
using AddressSetLib for AddressSetLib.AddressSet;
// Custom errors
error UnsupportedCollateral();
error OnlyCallableFromLiveTradingProcessor();
error UnknownLiveTicket();
error InsufficientBalance();
error OnlyCallableFromSGPTradingProcessor();
error UnknownSGPTicket();
error SpeedMarketsAMMCreatorNotSet();
error DirectionsCannotBeEmpty();
error CallerNotAllowed();
error UnknownTicket();
error UnknownActiveTicket();
error InvalidAddress();
error FreeBetExpired();
error FreeBetNotExpired();
error UnknownSpeedMarketTicketOwner();
error OnlyCallableFromSpeedMarketsAMMCreator();
uint private constant MAX_APPROVAL = type(uint256).max;
ISportsAMMV2 public sportsAMM;
ILiveTradingProcessor public liveTradingProcessor;
mapping(address => mapping(address => uint)) public balancePerUserAndCollateral;
mapping(address => bool) public supportedCollateral;
mapping(address => address) public ticketToUser;
mapping(address => uint) public paidPerTicket;
mapping(bytes32 => address) public liveRequestsPerUser;
// stores active tickets per user
mapping(address => AddressSetLib.AddressSet) internal activeTicketsPerUser;
// stores resolved tickets per user
mapping(address => AddressSetLib.AddressSet) internal resolvedTicketsPerUser;
ISGPTradingProcessor public sgpTradingProcessor;
mapping(bytes32 => address) public sgpRequestsPerUser;
mapping(address => mapping(address => uint)) public freeBetExpiration;
uint public freeBetExpirationPeriod;
uint public freeBetExpirationUpgrade;
mapping(address => AddressSetLib.AddressSet) internal usersWithFreeBetPerCollateral;
IAddressManager public addressManager;
mapping(bytes32 => address) public speedMarketRequestToUser;
// stores active speed markets per user
mapping(address => AddressSetLib.AddressSet) internal activeSpeedMarketsPerUser;
// stores resolved speed markets per user
mapping(address => AddressSetLib.AddressSet) internal resolvedSpeedMarketsPerUser;
// stores active chained speed markets per user
mapping(address => AddressSetLib.AddressSet) internal activeChainedSpeedMarketsPerUser;
// stores resolved chained speed markets per user
mapping(address => AddressSetLib.AddressSet) internal resolvedChainedSpeedMarketsPerUser;
/* ========== CONSTRUCTOR ========== */
function initialize(address _owner, address _sportsAMMV2, address _liveTradingProcessor) external initializer {
setOwner(_owner);
initNonReentrant();
sportsAMM = ISportsAMMV2(_sportsAMMV2);
liveTradingProcessor = ILiveTradingProcessor(_liveTradingProcessor);
}
/// @notice fund a batch of users with free bets in chosen collateral
function fundBatch(address[] calldata _users, address _collateral, uint _amountPerUser) external notPaused nonReentrant {
if (!supportedCollateral[_collateral]) revert UnsupportedCollateral();
IERC20(_collateral).safeTransferFrom(msg.sender, address(this), _amountPerUser * _users.length);
for (uint256 index; index < _users.length; ++index) {
address _user = _users[index];
_fundUser(_user, _collateral, _amountPerUser, msg.sender);
}
}
/// @notice fund a single user with free bets in chosen collateral
function fund(address _user, address _collateral, uint _amount) external notPaused nonReentrant {
if (!supportedCollateral[_collateral]) revert UnsupportedCollateral();
IERC20(_collateral).safeTransferFrom(msg.sender, address(this), _amount);
_fundUser(_user, _collateral, _amount, msg.sender);
}
/// @notice admin method to unallocate free bet that hasn't been used in a while
function removeUserFunding(
address _user,
address _collateral,
address _receiver
) external notPaused nonReentrant onlyOwner {
_removeUserFunding(_user, _collateral, _receiver);
}
/// @notice Removes expired free bet funds from multiple users and transfers them to the owner
/// @dev This function can be called by anyone, but only works for funds that have passed their expiration time
/// @param _users Array of user addresses whose expired funds will be removed
/// @param _collateral The token address of the collateral to be removed
function removeExpiredUserFunding(address[] calldata _users, address _collateral) external notPaused nonReentrant {
for (uint256 index; index < _users.length; ++index) {
address _user = _users[index];
if (balancePerUserAndCollateral[_user][_collateral] > 0) {
if (
!((freeBetExpiration[_user][_collateral] > 0 &&
freeBetExpiration[_user][_collateral] < block.timestamp) ||
(freeBetExpiration[_user][_collateral] == 0 &&
freeBetExpirationUpgrade + freeBetExpirationPeriod < block.timestamp))
) {
revert FreeBetNotExpired();
}
_removeUserFunding(_user, _collateral, owner);
}
}
}
/// @notice admin method to unallocate free bets that aren't used in a while
function removeUserFundingBatch(
address[] calldata _users,
address _collateral,
address _receiver
) external notPaused nonReentrant onlyOwner {
if (!supportedCollateral[_collateral]) revert UnsupportedCollateral();
for (uint256 index; index < _users.length; ++index) {
address _user = _users[index];
_removeUserFunding(_user, _collateral, _receiver);
}
}
function _removeUserFunding(address _user, address _collateral, address _receiver) internal {
if (!supportedCollateral[_collateral]) revert UnsupportedCollateral();
uint _amountRemoved = balancePerUserAndCollateral[_user][_collateral];
uint currentBalance = IERC20(_collateral).balanceOf(address(this));
if (_amountRemoved > 0 && currentBalance >= _amountRemoved) {
IERC20(_collateral).safeTransfer(_receiver, _amountRemoved);
}
balancePerUserAndCollateral[_user][_collateral] = 0;
if (usersWithFreeBetPerCollateral[_collateral].contains(_user)) {
usersWithFreeBetPerCollateral[_collateral].remove(_user);
}
emit UserFundingRemoved(_user, _collateral, _receiver, _amountRemoved);
}
/// @notice buy a system bet ticket for a user if he has enough free bet in given collateral
function tradeSystemBet(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral,
uint8 _systemBetDenominator
) external notPaused nonReentrant canTrade(msg.sender, _collateral, _buyInAmount) {
_trade(_tradeData, _buyInAmount, _expectedQuote, _additionalSlippage, _referrer, _collateral, _systemBetDenominator);
}
/// @notice buy a ticket for a user if he has enough free bet in given collateral
function trade(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral
) external notPaused nonReentrant canTrade(msg.sender, _collateral, _buyInAmount) {
_trade(_tradeData, _buyInAmount, _expectedQuote, _additionalSlippage, _referrer, _collateral, 0);
}
function _trade(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral,
uint8 _systemBetDenominator
) internal {
balancePerUserAndCollateral[msg.sender][_collateral] -= _buyInAmount;
address _createdTicket;
if (_systemBetDenominator > 0) {
_createdTicket = sportsAMM.tradeSystemBet(
_tradeData,
_buyInAmount,
_expectedQuote,
_additionalSlippage,
_referrer,
_collateral,
false,
_systemBetDenominator
);
} else {
_createdTicket = sportsAMM.trade(
_tradeData,
_buyInAmount,
_expectedQuote,
_additionalSlippage,
_referrer,
_collateral,
false
);
}
ticketToUser[_createdTicket] = msg.sender;
activeTicketsPerUser[msg.sender].add(_createdTicket);
emit FreeBetTrade(_createdTicket, _buyInAmount, msg.sender, false);
}
/// @notice request a live ticket for a user if he has enough free bet in given collateral
function tradeLive(
ILiveTradingProcessor.LiveTradeData calldata _liveTradeData
) external notPaused canTrade(msg.sender, _liveTradeData._collateral, _liveTradeData._buyInAmount) {
bytes32 _requestId = liveTradingProcessor.requestLiveTrade(_liveTradeData);
liveRequestsPerUser[_requestId] = msg.sender;
emit FreeBetLiveTradeRequested(msg.sender, _liveTradeData._buyInAmount, _requestId);
}
/// @notice request a live parlay ticket for a user if he has enough free bet in given collateral
function tradeLiveParlay(
ILiveTradingProcessor.LiveParlayTradeData calldata _liveParlayTradeData
) external notPaused canTrade(msg.sender, _liveParlayTradeData.collateral, _liveParlayTradeData.buyInAmount) {
bytes32 _requestId = liveTradingProcessor.requestLiveParlayTrade(_liveParlayTradeData);
liveRequestsPerUser[_requestId] = msg.sender;
emit FreeBetLiveParlayTradeRequested(
msg.sender,
_liveParlayTradeData.buyInAmount,
_requestId,
uint16(_liveParlayTradeData.legs.length)
);
}
/// @notice confirm a live ticket purchase. As live betting is a 2 step approach, the LiveTradingProcessor needs this method as callback so that the correct amount is deducted from the user's balance
function confirmLiveTrade(
bytes32 requestId,
address _createdTicket,
uint _buyInAmount,
address _collateral
) external notPaused nonReentrant {
if (msg.sender != address(liveTradingProcessor)) revert OnlyCallableFromLiveTradingProcessor();
address _user = liveRequestsPerUser[requestId];
if (_user == address(0)) revert UnknownLiveTicket();
if (_collateral == address(0)) {
_collateral = address(sportsAMM.defaultCollateral());
}
if (!supportedCollateral[_collateral]) revert UnsupportedCollateral();
if (balancePerUserAndCollateral[_user][_collateral] < _buyInAmount) revert InsufficientBalance();
balancePerUserAndCollateral[_user][_collateral] -= _buyInAmount;
ticketToUser[_createdTicket] = _user;
activeTicketsPerUser[_user].add(_createdTicket);
emit FreeBetTrade(_createdTicket, _buyInAmount, _user, true);
}
/// @notice request a sgp ticket for a user if he has enough free bet in given collateral
function tradeSGP(
ISGPTradingProcessor.SGPTradeData calldata _sgpTradeData
) external notPaused canTrade(msg.sender, _sgpTradeData._collateral, _sgpTradeData._buyInAmount) {
bytes32 _requestId = sgpTradingProcessor.requestSGPTrade(_sgpTradeData);
sgpRequestsPerUser[_requestId] = msg.sender;
emit FreeBetSGPTradeRequested(msg.sender, _sgpTradeData._buyInAmount, _requestId);
}
/// @notice confirm a SGP ticket purchase. As SGP betting is a 2 step approach, the SGPradingProcessor needs this method as callback so that the correct amount is deducted from the user's balance
function confirmSGPTrade(
bytes32 requestId,
address _createdTicket,
uint _buyInAmount,
address _collateral
) external notPaused nonReentrant {
if (msg.sender != address(sgpTradingProcessor)) revert OnlyCallableFromSGPTradingProcessor();
address _user = sgpRequestsPerUser[requestId];
if (_user == address(0)) revert UnknownSGPTicket();
if (_collateral == address(0)) {
_collateral = address(sportsAMM.defaultCollateral());
}
if (!supportedCollateral[_collateral]) revert UnsupportedCollateral();
if (balancePerUserAndCollateral[_user][_collateral] < _buyInAmount) revert InsufficientBalance();
balancePerUserAndCollateral[_user][_collateral] -= _buyInAmount;
ticketToUser[_createdTicket] = _user;
activeTicketsPerUser[_user].add(_createdTicket);
emit FreeBetTrade(_createdTicket, _buyInAmount, _user, true);
}
/// @notice create a pending speed market for a user if he has enough free bet in given collateral
function tradeSpeedMarket(
ISpeedMarketsAMMCreator.SpeedMarketParams calldata _params
) external notPaused nonReentrant canTrade(msg.sender, _params.collateral, _params.buyinAmount) {
address speedMarketsAMMCreator = addressManager.getAddress("SpeedMarketsAMMCreator");
if (speedMarketsAMMCreator == address(0)) revert SpeedMarketsAMMCreatorNotSet();
bytes32 _requestId = ISpeedMarketsAMMCreator(speedMarketsAMMCreator).addPendingSpeedMarket(_params);
speedMarketRequestToUser[_requestId] = msg.sender;
emit FreeBetSpeedMarketTradeRequested(
msg.sender,
_requestId,
_params.buyinAmount,
_params.asset,
_params.strikeTime,
_params.direction
);
}
/// @notice create a pending chained speed market for a user if he has enough free bet in given collateral
function tradeChainedSpeedMarket(
ISpeedMarketsAMMCreator.ChainedSpeedMarketParams calldata _params
) external notPaused nonReentrant canTrade(msg.sender, _params.collateral, _params.buyinAmount) {
address speedMarketsAMMCreator = addressManager.getAddress("SpeedMarketsAMMCreator");
if (speedMarketsAMMCreator == address(0)) revert SpeedMarketsAMMCreatorNotSet();
if (_params.directions.length == 0) revert DirectionsCannotBeEmpty();
bytes32 _requestId = ISpeedMarketsAMMCreator(speedMarketsAMMCreator).addPendingChainedSpeedMarket(_params);
speedMarketRequestToUser[_requestId] = msg.sender;
emit FreeBetChainedSpeedMarketTradeRequested(
msg.sender,
_requestId,
_params.buyinAmount,
_params.asset,
_params.timeFrame,
_params.directions.length
);
}
/// @notice confirm a speed or chained speed market trade. Called by SpeedMarketsAMMCreator as callback
/// @param requestId the request id of the pending speed market
/// @param _createdMarket the address of the created speed market
/// @param _collateral the address of the collateral
/// @param _buyInAmount the buy in amount
/// @param _isChainedSpeedMarket true if this is a chained speed market
function confirmSpeedOrChainedSpeedMarketTrade(
bytes32 requestId,
address _createdMarket,
address _collateral,
uint _buyInAmount,
bool _isChainedSpeedMarket
) external notPaused nonReentrant {
address speedMarketsAMMCreator = addressManager.getAddress("SpeedMarketsAMMCreator");
if (msg.sender != speedMarketsAMMCreator) revert OnlyCallableFromSpeedMarketsAMMCreator();
if (_collateral == address(0)) {
ISpeedMarketsAMM speedMarketsAMM = ISpeedMarketsAMM(addressManager.getAddress("SpeedMarketsAMM"));
_collateral = speedMarketsAMM.sUSD();
}
if (!supportedCollateral[_collateral]) revert UnsupportedCollateral();
address _user = speedMarketRequestToUser[requestId];
if (_user == address(0)) revert UnknownSpeedMarketTicketOwner();
if (balancePerUserAndCollateral[_user][_collateral] < _buyInAmount) revert InsufficientBalance();
balancePerUserAndCollateral[_user][_collateral] -= _buyInAmount;
ticketToUser[_createdMarket] = _user;
if (_isChainedSpeedMarket) {
activeChainedSpeedMarketsPerUser[_user].add(_createdMarket);
} else {
activeSpeedMarketsPerUser[_user].add(_createdMarket);
}
emit FreeBetSpeedTrade(_createdMarket, _buyInAmount, _user);
}
/// @notice callback from sportsAMM on ticket exercize if owner is this contract. The net winnings are sent to users while the freebet amount goes to the contract owner
/// @param _resolvedTicket the address of the resolved ticket
function confirmTicketResolved(address _resolvedTicket) external {
if (msg.sender != address(sportsAMM)) revert CallerNotAllowed();
address _user = ticketToUser[_resolvedTicket];
if (_user == address(0)) revert UnknownTicket();
if (!activeTicketsPerUser[_user].contains(_resolvedTicket)) revert UnknownActiveTicket();
uint _earned;
uint _exercized = Ticket(_resolvedTicket).finalPayout();
IERC20 _collateral = Ticket(_resolvedTicket).collateral();
uint buyInAmount = Ticket(_resolvedTicket).buyInAmount();
_earned = _resolveMarket(_user, _collateral, _exercized, buyInAmount);
activeTicketsPerUser[_user].remove(_resolvedTicket);
resolvedTicketsPerUser[_user].add(_resolvedTicket);
emit FreeBetTicketResolved(_resolvedTicket, _user, _earned);
}
/// @notice callback from SpeedMarketsAMMResolver on speed market resolution. Net winnings are sent to users while the freebet amount goes to the contract owner
/// @param _resolvedSpeedMarket the address of the resolved speed market
/// @param _exercized the amount exercised from the speed market
/// @param _buyInAmount the original buy in amount
/// @param _collateral the address of the collateral
/// @param isChained true if this is a chained speed market
function confirmSpeedMarketResolved(
address _resolvedSpeedMarket,
uint _exercized,
uint _buyInAmount,
address _collateral,
bool isChained
) external {
address speedMarketsAMMResolver = addressManager.getAddress("SpeedMarketsAMMResolver");
if (msg.sender != speedMarketsAMMResolver) revert CallerNotAllowed();
address _user = ticketToUser[_resolvedSpeedMarket];
if (_user == address(0)) revert UnknownTicket();
uint earned = _resolveMarket(_user, IERC20(_collateral), _exercized, _buyInAmount);
if (isChained) {
if (!activeChainedSpeedMarketsPerUser[_user].contains(_resolvedSpeedMarket)) revert UnknownActiveTicket();
activeChainedSpeedMarketsPerUser[_user].remove(_resolvedSpeedMarket);
resolvedChainedSpeedMarketsPerUser[_user].add(_resolvedSpeedMarket);
} else {
if (!activeSpeedMarketsPerUser[_user].contains(_resolvedSpeedMarket)) revert UnknownActiveTicket();
activeSpeedMarketsPerUser[_user].remove(_resolvedSpeedMarket);
resolvedSpeedMarketsPerUser[_user].add(_resolvedSpeedMarket);
}
emit FreeBetSpeedMarketResolved(_resolvedSpeedMarket, _user, earned);
}
/// @notice admin method to retrieve stuck funds if needed
function retrieveFunds(IERC20 _collateral, uint _amount) external onlyOwner {
_collateral.safeTransfer(msg.sender, _amount);
}
/* ========== SETTERS ========== */
/// @notice add or remove a supported collateral for address
/// @param _collateral the address of the collateral
/// @param _supported true if the collateral is supported, false otherwise
/// @param addressToApprove the address to approve
function addSupportedCollateral(address _collateral, bool _supported, address addressToApprove) external onlyOwner {
supportedCollateral[_collateral] = _supported;
if (_supported) {
IERC20(_collateral).approve(addressToApprove, MAX_APPROVAL);
} else {
IERC20(_collateral).approve(addressToApprove, 0);
}
emit CollateralSupportChanged(_collateral, _supported, addressToApprove);
}
/* ========== GETTERS ========== */
/// @notice gets batch of active tickets per user
/// @param _index start index
/// @param _pageSize batch size
/// @param _user to get active tickets for
/// @return activeTickets
function getActiveTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory) {
return activeTicketsPerUser[_user].getPage(_index, _pageSize);
}
/// @notice gets batch of active speed markets per user
/// @param _index start index
/// @param _pageSize batch size
/// @param _user to get active speed markets for
/// @return activeSpeedMarkets
function getActiveSpeedMarketsPerUser(
uint _index,
uint _pageSize,
address _user
) external view returns (address[] memory) {
return activeSpeedMarketsPerUser[_user].getPage(_index, _pageSize);
}
/// @notice gets batch of active chained speed markets per user
/// @param _index start index
/// @param _pageSize batch size
/// @param _user to get active chained speed markets for
/// @return activeChainedSpeedMarkets
function getActiveChainedSpeedMarketsPerUser(
uint _index,
uint _pageSize,
address _user
) external view returns (address[] memory) {
return activeChainedSpeedMarketsPerUser[_user].getPage(_index, _pageSize);
}
/// @notice gets number of active tickets per user
/// @param _user to get number of active tickets for
/// @return numOfActiveTickets
function numOfActiveTicketsPerUser(address _user) external view returns (uint) {
return activeTicketsPerUser[_user].elements.length;
}
/// @notice gets number of active speed markets per user
/// @param _user to get number of active speed markets for
/// @return numOfActiveSpeedMarkets
function numOfActiveSpeedMarketsPerUser(address _user) external view returns (uint) {
return activeSpeedMarketsPerUser[_user].elements.length;
}
/// @notice gets number of active chained speed markets per user
/// @param _user to get number of active speed markets for
/// @return numOfActiveChainedSpeedMarkets
function numOfActiveChainedSpeedMarketsPerUser(address _user) external view returns (uint) {
return activeChainedSpeedMarketsPerUser[_user].elements.length;
}
/// @notice gets batch of resolved tickets per user
/// @param _index start index
/// @param _pageSize batch size
/// @param _user to get resolved tickets for
/// @return resolvedTickets
function getResolvedTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory) {
return resolvedTicketsPerUser[_user].getPage(_index, _pageSize);
}
/// @notice gets batch of resolved speed markets per user
/// @param _index start index
/// @param _pageSize batch size
/// @param _user to get resolved speed markets for
/// @return resolvedSpeedMarkets
function getResolvedSpeedMarketsPerUser(
uint _index,
uint _pageSize,
address _user
) external view returns (address[] memory) {
return resolvedSpeedMarketsPerUser[_user].getPage(_index, _pageSize);
}
/// @notice gets batch of resolved speed markets per user
/// @param _index start index
/// @param _pageSize batch size
/// @param _user to get resolved speed markets for
/// @return resolvedSpeedMarkets
function getResolvedChainedSpeedMarketsPerUser(
uint _index,
uint _pageSize,
address _user
) external view returns (address[] memory) {
return resolvedChainedSpeedMarketsPerUser[_user].getPage(_index, _pageSize);
}
/// @notice gets number of resolved tickets per user
/// @param _user to get number of resolved tickets for
/// @return numOfResolvedTickets
function numOfResolvedTicketsPerUser(address _user) external view returns (uint) {
return resolvedTicketsPerUser[_user].elements.length;
}
/// @notice gets number of resolved speed markets per user
/// @param _user to get number of resolved speed markets for
/// @return numOfResolvedSpeedMarkets
function numOfResolvedSpeedMarketsPerUser(address _user) external view returns (uint) {
return resolvedSpeedMarketsPerUser[_user].elements.length;
}
/// @notice gets number of resolved speed markets per user
/// @param _user to get number of resolved speed markets for
/// @return numOfResolvedSpeedMarkets
function numOfResolvedChainedSpeedMarketsPerUser(address _user) external view returns (uint) {
return resolvedChainedSpeedMarketsPerUser[_user].elements.length;
}
/// @notice checks if a free bet is valid
/// @param _user the address of the user
/// @param _collateral the address of the collateral
/// @return isValid true if the free bet is valid, false otherwise
/// @return timeToExpiration the time to expiration of the free bet, 0 if the free bet is not valid
function isFreeBetValid(address _user, address _collateral) external view returns (bool isValid, uint timeToExpiration) {
(isValid, timeToExpiration) = _isFreeBetValidAndTimeToExpiration(_user, _collateral);
}
/// @notice get users with free bet per collateral
/// @param _collateral the address of the collateral
/// @param _index the start index
/// @param _pageSize the page size
/// @return users
function getUsersWithFreeBetPerCollateral(
address _collateral,
uint _index,
uint _pageSize
) external view returns (address[] memory) {
return usersWithFreeBetPerCollateral[_collateral].getPage(_index, _pageSize);
}
/// @notice get number of users with free bet per collateral
/// @param _collateral the address of the collateral
/// @return number of users
function numOfUsersWithFreeBetPerCollateral(address _collateral) external view returns (uint) {
return usersWithFreeBetPerCollateral[_collateral].elements.length;
}
/// @notice Get users with free bet per collateral, the free bet amount, if it's valid and the time to expiration
/// @param _collateral the address of the collateral
/// @param _index the start index
/// @param _pageSize the page size
/// @return allUsers
/// @return freeBetAmounts
/// @return isValid
/// @return timeToExpiration
function getUsersFreeBetDataPerCollateral(
address _collateral,
uint _index,
uint _pageSize
)
external
view
returns (
address[] memory allUsers,
uint[] memory freeBetAmounts,
bool[] memory isValid,
uint[] memory timeToExpiration
)
{
if (_pageSize > usersWithFreeBetPerCollateral[_collateral].elements.length) {
_pageSize = usersWithFreeBetPerCollateral[_collateral].elements.length;
}
allUsers = new address[](_pageSize);
isValid = new bool[](_pageSize);
freeBetAmounts = new uint[](_pageSize);
timeToExpiration = new uint[](_pageSize);
for (uint i; i < _pageSize; ++i) {
address user = usersWithFreeBetPerCollateral[_collateral].elements[_index + i];
(isValid[i], timeToExpiration[i]) = _isFreeBetValidAndTimeToExpiration(user, _collateral);
allUsers[i] = user;
freeBetAmounts[i] = balancePerUserAndCollateral[user][_collateral];
}
}
/* ========== SETTERS ========== */
/// @notice sets the LiveTradingProcessor contract address
/// @param _liveTradingProcessor the address of Live Trading Processor contract
function setLiveTradingProcessor(address _liveTradingProcessor) external onlyOwner {
if (_liveTradingProcessor == address(0)) revert InvalidAddress();
liveTradingProcessor = ILiveTradingProcessor(_liveTradingProcessor);
emit SetLiveTradingProcessor(_liveTradingProcessor);
}
/// @notice sets the SGPTradingProcessor contract address
/// @param _sgpTradingProcessor the address of SGP Trading Processor contract
function setSGPTradingProcessor(address _sgpTradingProcessor) external onlyOwner {
if (_sgpTradingProcessor == address(0)) revert InvalidAddress();
sgpTradingProcessor = ISGPTradingProcessor(_sgpTradingProcessor);
emit SetSGPTradingProcessor(_sgpTradingProcessor);
}
/// @notice sets the Sports AMM contract address
/// @param _sportsAMM the address of Sports AMM contract
function setSportsAMM(address _sportsAMM) external onlyOwner {
if (_sportsAMM == address(0)) revert InvalidAddress();
sportsAMM = ISportsAMMV2(_sportsAMM);
emit SetSportsAMM(_sportsAMM);
}
/// @notice sets the free bet expiration period
/// @param _freeBetExpirationPeriod the new free bet expiration period
function setFreeBetExpirationPeriod(uint _freeBetExpirationPeriod, uint _freeBetExpirationUpgrade) external onlyOwner {
freeBetExpirationPeriod = _freeBetExpirationPeriod;
freeBetExpirationUpgrade = _freeBetExpirationUpgrade == 0 ? block.timestamp : _freeBetExpirationUpgrade;
emit SetFreeBetExpirationPeriod(_freeBetExpirationPeriod, _freeBetExpirationUpgrade);
}
/// @notice sets the free bet expiration for a user
/// @param _user the address of the user
/// @param _collateral the address of the collateral
/// @param _freeBetExpiration the new free bet expiration
function setUserFreeBetExpiration(address _user, address _collateral, uint _freeBetExpiration) external onlyOwner {
freeBetExpiration[_user][_collateral] = _freeBetExpiration;
}
/// @notice sets the users with free bet per collateral
/// @param _users the addresses of the users
/// @param _collateral the address of the collateral
function setUsersWithAlreadyFundedFreeBetPerCollateral(
address[] calldata _users,
address _collateral
) external onlyOwner {
for (uint i; i < _users.length; ++i) {
usersWithFreeBetPerCollateral[_collateral].add(_users[i]);
}
}
/// @notice sets the Address Manager contract address
/// @param _addressManager the address of Address Manager contract
function setAddressManager(address _addressManager) external onlyOwner {
if (_addressManager == address(0)) revert InvalidAddress();
addressManager = IAddressManager(_addressManager);
emit SetAddressManager(_addressManager);
}
/* ========== INTERNAL FUNCTIONS ========== */
function _resolveMarket(
address _user,
IERC20 _collateral,
uint _exercized,
uint _buyInAmount
) internal returns (uint earned) {
if (_exercized > 0) {
if (_exercized > _buyInAmount) {
_collateral.safeTransfer(owner, _buyInAmount);
earned = _exercized - _buyInAmount;
if (earned > 0) {
_collateral.safeTransfer(_user, earned);
}
} else {
balancePerUserAndCollateral[_user][address(_collateral)] += _exercized;
}
}
}
function _fundUser(address _user, address _collateral, uint _amount, address _sender) internal {
usersWithFreeBetPerCollateral[_collateral].add(_user);
balancePerUserAndCollateral[_user][_collateral] += _amount;
freeBetExpiration[_user][_collateral] = block.timestamp + freeBetExpirationPeriod;
emit UserFunded(_user, _collateral, _amount, _sender);
}
function _isFreeBetValidAndTimeToExpiration(
address _user,
address _collateral
) internal view returns (bool isValid, uint timeToExpiration) {
if (supportedCollateral[_collateral] && balancePerUserAndCollateral[_user][_collateral] > 0) {
uint expirationDate = freeBetExpiration[_user][_collateral] > 0
? freeBetExpiration[_user][_collateral]
: freeBetExpirationUpgrade + freeBetExpirationPeriod;
isValid = expirationDate > block.timestamp;
timeToExpiration = isValid ? expirationDate - block.timestamp : 0;
}
}
function _isFreeBetValid(address _user, address _collateral) internal view returns (bool) {
return
freeBetExpiration[_user][_collateral] > block.timestamp ||
(freeBetExpiration[_user][_collateral] == 0 &&
freeBetExpirationUpgrade + freeBetExpirationPeriod > block.timestamp);
}
/* ========== MODIFIERS ========== */
modifier canTrade(
address _user,
address _collateral,
uint _amount
) {
if (!supportedCollateral[_collateral]) revert UnsupportedCollateral();
if (balancePerUserAndCollateral[_user][_collateral] < _amount) revert InsufficientBalance();
if (!_isFreeBetValid(_user, _collateral)) revert FreeBetExpired();
_;
}
/* ========== EVENTS ========== */
event SetSportsAMM(address sportsAMM);
event SetLiveTradingProcessor(address liveTradingProcessor);
event SetSGPTradingProcessor(address sgpTradingProcessor);
event SetAddressManager(address addressManager);
event UserFunded(address user, address collateral, uint amount, address funder);
event FreeBetTrade(address createdTicket, uint buyInAmount, address user, bool isLive);
event FreeBetSpeedTrade(address createdSpeedMarket, uint buyInAmount, address user);
event CollateralSupportChanged(address collateral, bool supported, address addressToApprove);
event FreeBetTicketResolved(address ticket, address user, uint earned);
event FreeBetSpeedMarketResolved(address speedMarket, address user, uint earned);
event FreeBetLiveTradeRequested(address user, uint buyInAmount, bytes32 requestId);
event FreeBetLiveParlayTradeRequested(address user, uint buyInAmount, bytes32 requestId, uint16 legsCount);
event FreeBetSGPTradeRequested(address user, uint buyInAmount, bytes32 requestId);
event FreeBetSpeedMarketTradeRequested(
address user,
bytes32 requestId,
uint buyInAmount,
bytes32 asset,
uint64 strikeTime,
ISpeedMarketsAMMCreator.Direction direction
);
event FreeBetChainedSpeedMarketTradeRequested(
address user,
bytes32 requestId,
uint buyInAmount,
bytes32 asset,
uint64 timeFrame,
uint directionsCount
);
event UserFundingRemoved(address _user, address _collateral, address _receiver, uint _amount);
event SetFreeBetExpirationPeriod(uint freeBetExpirationPeriod, uint freeBetExpirationUpgrade);
event UpdateMaxApprovalSpeedMarketsAMM(address collateral);
}// 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) (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 IAddressManager {
struct Addresses {
address safeBox;
address referrals;
address stakingThales;
address multiCollateralOnOffRamp;
address pyth;
address speedMarketsAMM;
}
function safeBox() external view returns (address);
function referrals() external view returns (address);
function stakingThales() external view returns (address);
function multiCollateralOnOffRamp() external view returns (address);
function pyth() external view returns (address);
function speedMarketsAMM() external view returns (address);
function getAddresses() external view returns (Addresses memory);
function getAddresses(string[] calldata _contractNames) external view returns (address[] memory contracts);
function getAddress(string memory _contractName) external view returns (address contract_);
function checkIfContractExists(string memory _contractName) external view returns (bool contractExists);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// internal
import "../../interfaces/ISportsAMMV2Manager.sol";
import "../../interfaces/ISportsAMMV2.sol";
contract Ticket {
using SafeERC20 for IERC20;
uint private constant ONE = 1e18;
enum Phase {
Trading,
Maturity,
Expiry
}
struct MarketData {
bytes32 gameId;
uint16 sportId;
uint16 typeId;
uint maturity;
uint8 status;
int24 line;
uint24 playerId;
uint8 position;
uint odd;
ISportsAMMV2.CombinedPosition[] combinedPositions;
}
struct TicketInit {
MarketData[] _markets;
uint _buyInAmount;
uint _fees;
uint _totalQuote;
address _sportsAMM;
address _ticketOwner;
IERC20 _collateral;
uint _expiry;
bool _isLive;
uint8 _systemBetDenominator;
bool _isSGP;
}
ISportsAMMV2 public sportsAMM;
address public ticketOwner;
IERC20 public collateral;
uint public buyInAmount;
uint public fees;
uint public totalQuote;
uint public numOfMarkets;
uint public expiry;
uint public createdAt;
bool public resolved;
bool public paused;
bool public initialized;
bool public cancelled;
bool public isLive;
mapping(uint => MarketData) public markets;
uint public finalPayout;
bool public isSystem;
uint8 public systemBetDenominator;
bool public isSGP;
bool public isMarkedAsLost;
uint public expectedFinalPayout;
/* ========== CONSTRUCTOR and INITIALIZERS========== */
/// @notice initialize the ticket contract
/// @param params all parameters for Init
function initialize(TicketInit calldata params) external {
require(!initialized, "Ticket already initialized");
initialized = true;
sportsAMM = ISportsAMMV2(params._sportsAMM);
numOfMarkets = params._markets.length;
for (uint i = 0; i < numOfMarkets; i++) {
markets[i] = params._markets[i];
}
buyInAmount = params._buyInAmount;
fees = params._fees;
totalQuote = params._totalQuote;
ticketOwner = params._ticketOwner;
collateral = params._collateral;
expiry = params._expiry;
isLive = params._isLive;
createdAt = block.timestamp;
systemBetDenominator = params._systemBetDenominator;
isSystem = systemBetDenominator > 0;
isSGP = params._isSGP;
}
/**
* @notice Sets the expected final payout amount for this ticket.
* @dev
* - Can only be called by the SportsAMM contract.
* - This value represents the total amount of collateral (including fees)
* that was initially funded to the ticket upon creation.
* - Used later in `exercise()` to prevent manipulation or overfunding attacks,
* ensuring payout calculations rely only on the original committed collateral
* and not on the current token balance of the contract.
* - Once set, this value should remain constant throughout the ticket lifecycle.
*
* @param amount The total expected collateral amount that should be held by this ticket.
* Must include both user buy-in and fees.
*
* Emits a {ExpectedFinalPayoutSet} event.
*/
function setExpectedFinalPayout(uint amount) external onlyAMM {
expectedFinalPayout = amount;
emit ExpectedFinalPayoutSet(amount);
}
/* ========== EXTERNAL READ FUNCTIONS ========== */
/// @notice checks if the user lost the ticket
/// @return isTicketLost true/false
function isTicketLost() public view returns (bool) {
if (isMarkedAsLost) {
return true;
} else {
uint lostMarketsCount = 0;
for (uint i = 0; i < numOfMarkets; i++) {
(bool isMarketResolved, bool isWinningMarketPosition) = sportsAMM
.resultManager()
.isMarketResolvedAndPositionWinning(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
if (isMarketResolved && !isWinningMarketPosition) {
if (!isSystem) {
return true;
} else {
lostMarketsCount++;
if (lostMarketsCount > (numOfMarkets - systemBetDenominator)) {
return true;
}
}
}
}
return false;
}
}
/// @notice checks are all markets of the ticket resolved
/// @return areAllMarketsResolved true/false
function areAllMarketsResolved() public view returns (bool) {
for (uint i = 0; i < numOfMarkets; i++) {
if (
!sportsAMM.resultManager().isMarketResolved(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].combinedPositions
)
) {
return false;
}
}
return true;
}
/// @notice checks if the user won the ticket
/// @return hasUserWon true/false
function isUserTheWinner() external view returns (bool hasUserWon) {
hasUserWon = _isUserTheWinner();
}
/// @notice checks if the ticket ready to be exercised
/// @return isExercisable true/false
function isTicketExercisable() public view returns (bool isExercisable) {
isExercisable = !resolved && (areAllMarketsResolved() || isTicketLost());
}
/// @notice gets current phase of the ticket
/// @return phase ticket phase
function phase() public view returns (Phase) {
return
isTicketExercisable() || resolved ? ((expiry < block.timestamp) ? Phase.Expiry : Phase.Maturity) : Phase.Trading;
}
/// @notice gets combined positions of the game
/// @return combinedPositions game combined positions
function getCombinedPositions(
uint _marketIndex
) public view returns (ISportsAMMV2.CombinedPosition[] memory combinedPositions) {
return markets[_marketIndex].combinedPositions;
}
/// @notice return the payout for this ticket
/// @return systemBetPayout the payout for this ticket
function getSystemBetPayout() external view returns (uint systemBetPayout) {
systemBetPayout = _getSystemBetPayout();
}
/* ========== EXTERNAL WRITE FUNCTIONS ========== */
/// @notice exercise ticket
function exercise(address _exerciseCollateral) external onlyAMM notPaused returns (uint) {
bool isExercisable = isTicketExercisable();
require(isExercisable, "Ticket not exercisable yet");
require(expectedFinalPayout > 0, "Expected final payout not set");
uint payoutWithFees = expectedFinalPayout;
uint payout = payoutWithFees - fees;
bool isCancelled = false;
if (_isUserTheWinner()) {
finalPayout = payout;
isCancelled = true;
for (uint i = 0; i < numOfMarkets; i++) {
bool isCancelledMarketPosition = sportsAMM.resultManager().isCancelledMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
if (isCancelledMarketPosition) {
if (isSGP) {
isCancelled = true;
break;
}
finalPayout = (finalPayout * markets[i].odd) / ONE;
} else {
isCancelled = false;
}
}
finalPayout = isCancelled ? buyInAmount : (isSystem ? _getSystemBetPayout() : finalPayout);
collateral.safeTransfer(
_exerciseCollateral == address(0) || _exerciseCollateral == address(collateral)
? address(ticketOwner)
: address(sportsAMM),
finalPayout
);
}
// if user is lost or if the user payout was less than anticipated due to cancelled games, send the remainder to AMM
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(address(sportsAMM), balance);
}
_resolve(!isTicketLost(), isCancelled);
return finalPayout;
}
/// @notice expire ticket
function expire(address _beneficiary) external onlyAMM {
require(phase() == Phase.Expiry, "Ticket not in expiry phase");
require(!resolved, "Can't expire resolved ticket");
emit Expired(_beneficiary);
_selfDestruct(_beneficiary);
}
/// @notice cancel the ticket
function cancel() external onlyAMM notPaused returns (uint) {
finalPayout = buyInAmount;
collateral.safeTransfer(address(ticketOwner), finalPayout);
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(address(sportsAMM), balance);
}
_resolve(true, true);
return finalPayout;
}
/// @notice mark the ticket as lost
function markAsLost() external onlyAMM notPaused returns (uint) {
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(address(sportsAMM), balance);
}
_resolve(false, false);
isMarkedAsLost = true;
return 0;
}
/// @notice withdraw collateral from the ticket
function withdrawCollateral(address recipient) external onlyAMM {
collateral.safeTransfer(recipient, collateral.balanceOf(address(this)));
}
/* ========== INTERNAL FUNCTIONS ========== */
function _resolve(bool _hasUserWon, bool _cancelled) internal {
resolved = true;
cancelled = _cancelled;
emit Resolved(_hasUserWon, _cancelled);
}
function _selfDestruct(address beneficiary) internal {
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(beneficiary, balance);
}
}
function _isUserTheWinner() internal view returns (bool hasUserWon) {
if (areAllMarketsResolved()) {
hasUserWon = !isTicketLost();
}
}
/* ========== SETTERS ========== */
function setPaused(bool _paused) external {
require(msg.sender == address(sportsAMM.manager()), "Invalid sender");
if (paused == _paused) return;
paused = _paused;
emit PauseUpdated(_paused);
}
/* ========== SYSTEM BET UTILS ========== */
function _getSystemBetPayout() internal view returns (uint systemBetPayout) {
if (isSystem) {
uint8[][] memory systemCombinations = sportsAMM.riskManager().generateCombinations(
uint8(numOfMarkets),
systemBetDenominator
);
uint totalCombinations = systemCombinations.length;
uint buyinPerCombination = ((buyInAmount * ONE) / totalCombinations) / ONE;
bool[] memory winningMarkets = new bool[](numOfMarkets);
bool[] memory cancelledMarkets = new bool[](numOfMarkets);
for (uint i = 0; i < numOfMarkets; i++) {
if (
!sportsAMM.resultManager().isMarketResolved(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].combinedPositions
)
) {
return 0;
}
winningMarkets[i] = sportsAMM.resultManager().isWinningMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
cancelledMarkets[i] = sportsAMM.resultManager().isCancelledMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
}
// Loop through each stored combination
for (uint i = 0; i < totalCombinations; i++) {
uint8[] memory currentCombination = systemCombinations[i];
uint combinationQuote = ONE;
for (uint j = 0; j < currentCombination.length; j++) {
uint8 marketIndex = currentCombination[j];
if (winningMarkets[marketIndex]) {
if (!cancelledMarkets[marketIndex]) {
combinationQuote = (combinationQuote * markets[marketIndex].odd) / ONE;
}
} else {
combinationQuote = 0;
break;
}
}
if (combinationQuote > 0) {
uint combinationPayout = (buyinPerCombination * ONE) / combinationQuote;
systemBetPayout += combinationPayout;
}
}
uint maxPayout = (buyInAmount * ONE) / totalQuote;
if (systemBetPayout > maxPayout) {
systemBetPayout = maxPayout;
}
}
}
/* ========== MODIFIERS ========== */
modifier onlyAMM() {
require(msg.sender == address(sportsAMM), "Only the AMM may perform these methods");
_;
}
modifier notPaused() {
require(!paused, "Market paused");
_;
}
/* ========== EVENTS ========== */
event Resolved(bool isUserTheWinner, bool cancelled);
event Expired(address beneficiary);
event PauseUpdated(bool paused);
event ExpectedFinalPayoutSet(uint amount);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IProxyBetting.sol";
interface IFreeBetsHolder is IProxyBetting {
function ticketToUser(address _createdTicket) external view returns (address);
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);
function confirmSpeedOrChainedSpeedMarketTrade(
bytes32 _requestId,
address _speedMarketAddress,
address _collateral,
uint _buyinAmount,
bool _isChained
) external;
function confirmSpeedMarketResolved(
address _resolvedTicket,
uint _exercized,
uint _buyInAmount,
address _collateral,
bool isChained
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface ILiveTradingProcessor {
struct LiveTradeData {
string _gameId;
uint16 _sportId;
uint16 _typeId;
int24 _line;
uint8 _position;
uint _buyInAmount;
uint _expectedQuote;
uint _additionalSlippage;
address _referrer;
address _collateral;
uint24 _playerId; // player props
}
struct LiveParlayLeg {
string gameId;
uint16 sportId;
uint16 typeId;
int24 line;
uint8 position;
uint expectedLegOdd; // optional; node-side hint
uint24 playerId;
}
struct LiveParlayTradeData {
LiveParlayLeg[] legs;
uint buyInAmount;
uint expectedPayout;
uint additionalSlippage;
address referrer;
address collateral;
}
// =========================
// Views
// =========================
function freeBetsHolder() external view returns (address);
function maxAllowedExecutionDelay() external view returns (uint);
function requestCounter() external view returns (uint);
function counterToRequestId(uint _counter) external view returns (bytes32);
function requestIdToRequester(bytes32 _requestId) external view returns (address);
function requestIdIsParlay(bytes32 _requestId) external view returns (bool);
function requestIdToTicketId(bytes32 _requestId) external view returns (address);
function requestIdFulfilled(bytes32 _requestId) external view returns (bool);
function timestampPerRequest(bytes32 _requestId) external view returns (uint);
function getTradeData(bytes32 _requestId) external view returns (LiveTradeData memory);
function getParlayTradeData(bytes32 _requestId) external view returns (LiveParlayTradeData memory);
// =========================
// Actions
// =========================
function requestLiveTrade(LiveTradeData calldata _liveTradeData) external returns (bytes32);
function requestLiveParlayTrade(LiveParlayTradeData calldata _parlay) external returns (bytes32);
/**
* @notice SINGLE fulfill (backwards-compatible with production)
*/
function fulfillLiveTrade(bytes32 _requestId, bool allow, uint approvedQuote) external;
/**
* @notice PARLAY fulfill (new)
* @dev approvedLegOdds.length must equal number of legs
*/
function fulfillLiveTradeParlay(
bytes32 _requestId,
bool allow,
uint approvedQuote,
uint[] calldata approvedLegOdds
) external;
}// 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 "./ISportsAMMV2.sol";
interface ISGPTradingProcessor {
struct SGPTradeData {
ISportsAMMV2.TradeData[] _tradeData;
uint _buyInAmount;
uint _expectedQuote;
uint _additionalSlippage;
address _referrer;
address _collateral;
}
function fulfillSGPTrade(bytes32 _requestId, bool allow, uint approvedAmount) external;
function requestSGPTrade(SGPTradeData calldata _sgpTradeData) external returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ISpeedMarketsAMM {
function sUSD() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ISpeedMarketsAMMCreator {
enum Direction {
Up,
Down
}
struct SpeedMarketParams {
bytes32 asset;
uint64 strikeTime;
uint64 delta;
uint strikePrice;
uint strikePriceSlippage;
Direction direction;
address collateral;
uint buyinAmount;
address referrer;
uint skewImpact;
}
struct PendingSpeedMarket {
address user;
bytes32 asset;
uint64 strikeTime;
uint64 delta;
uint strikePrice;
uint strikePriceSlippage;
Direction direction;
address collateral;
uint buyinAmount;
address referrer;
uint skewImpact;
uint256 createdAt;
}
struct ChainedSpeedMarketParams {
bytes32 asset;
uint64 timeFrame;
uint strikePrice;
uint strikePriceSlippage;
Direction[] directions;
address collateral;
uint buyinAmount;
address referrer;
}
struct PendingChainedSpeedMarket {
address user;
bytes32 asset;
uint64 timeFrame;
uint strikePrice;
uint strikePriceSlippage;
Direction[] directions;
address collateral;
uint buyinAmount;
address referrer;
uint256 createdAt;
}
function pendingSpeedMarkets(uint256 _index) external view returns (PendingSpeedMarket memory);
function pendingChainedSpeedMarkets(uint256 _index) external view returns (PendingChainedSpeedMarket memory);
function getPendingSpeedMarketsSize() external view returns (uint256);
function getPendingChainedSpeedMarketsSize() external view returns (uint256);
function addPendingSpeedMarket(SpeedMarketParams calldata _params) external returns (bytes32);
function addPendingChainedSpeedMarket(ChainedSpeedMarketParams calldata _params) external returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/ISportsAMMV2Manager.sol";
import "../interfaces/ISportsAMMV2ResultManager.sol";
import "../interfaces/ISportsAMMV2RiskManager.sol";
import "../interfaces/ISportsAMMV2Manager.sol";
import "../interfaces/IFreeBetsHolder.sol";
import "../interfaces/IStakingThalesBettingProxy.sol";
interface ISportsAMMV2 {
enum TicketAction {
Exercise,
Cancel,
MarkLost
}
struct CombinedPosition {
uint16 typeId;
uint8 position;
int24 line;
}
struct TradeData {
bytes32 gameId;
uint16 sportId;
uint16 typeId;
uint maturity;
uint8 status;
int24 line;
uint24 playerId;
uint[] odds;
bytes32[] merkleProof;
uint8 position;
CombinedPosition[][] combinedPositions;
}
function defaultCollateral() external view returns (IERC20);
function manager() external view returns (ISportsAMMV2Manager);
function resultManager() external view returns (ISportsAMMV2ResultManager);
function safeBoxFee() external view returns (uint);
function handleTicketResolving(address _ticket, ISportsAMMV2.TicketAction action) external;
function riskManager() external view returns (ISportsAMMV2RiskManager);
function freeBetsHolder() external view returns (IFreeBetsHolder);
function stakingThalesBettingProxy() external view returns (IStakingThalesBettingProxy);
function tradeLive(
TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
address _recipient,
address _referrer,
address _collateral
) external returns (address _createdTicket);
function trade(
TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral,
bool _isEth
) external returns (address _createdTicket);
function tradeSystemBet(
TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
address _collateral,
bool _isEth,
uint8 _systemBetDenominator
) external returns (address _createdTicket);
function tradeSGP(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _approvedQuote,
address _recipient,
address _referrer,
address _collateral
) external returns (address _createdTicket);
function rootPerGame(bytes32 game) external view returns (bytes32);
function getRootsPerGames(bytes32[] calldata _games) external view returns (bytes32[] memory _roots);
function paused() external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./ISportsAMMV2.sol";
interface ISportsAMMV2Manager {
enum Role {
ROOT_SETTING,
RISK_MANAGING,
MARKET_RESOLVING,
TICKET_PAUSER
}
function isWhitelistedAddress(address _address, Role role) external view returns (bool);
function decimals() external view returns (uint);
function feeToken() external view returns (address);
function isActiveTicket(address _ticket) external view returns (bool);
function getActiveTickets(uint _index, uint _pageSize) external view returns (address[] memory);
function numOfActiveTickets() external view returns (uint);
function getActiveTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);
function numOfActiveTicketsPerUser(address _user) external view returns (uint);
function getResolvedTicketsPerUser(uint _index, uint _pageSize, address _user) external view returns (address[] memory);
function numOfResolvedTicketsPerUser(address _user) external view returns (uint);
function getTicketsPerGame(uint _index, uint _pageSize, bytes32 _gameId) external view returns (address[] memory);
function numOfTicketsPerGame(bytes32 _gameId) external view returns (uint);
function isKnownTicket(address _ticket) external view returns (bool);
function sportsAMM() external view returns (address);
function getTicketsPerMarket(
uint _index,
uint _pageSize,
bytes32 _gameId,
uint _typeId,
uint _playerId
) external view returns (address[] memory);
function numOfTicketsPerMarket(bytes32 _gameId, uint _typeId, uint _playerId) external view returns (uint);
function addNewKnownTicket(ISportsAMMV2.TradeData[] memory _tradeData, address ticket, address user) external;
function resolveKnownTicket(address ticket, address ticketOwner) external;
function expireKnownTicket(address ticket, address ticketOwner) external;
function isSystemTicket(address _ticket) external view returns (bool);
function isSGPTicket(address _ticket) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ISportsAMMV2.sol";
interface ISportsAMMV2ResultManager {
enum MarketPositionStatus {
Open,
Cancelled,
Winning,
Losing
}
function isMarketResolved(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
ISportsAMMV2.CombinedPosition[] memory combinedPositions
) external view returns (bool isResolved);
function getMarketPositionStatus(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (MarketPositionStatus status);
function isWinningMarketPosition(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (bool isWinning);
function isCancelledMarketPosition(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (bool isCancelled);
function getResultsPerMarket(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId
) external view returns (int24[] memory results);
function resultTypePerMarketType(uint _typeId) external view returns (uint8 marketType);
function isMarketResolvedAndPositionWinning(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (bool isResolved, bool isWinning);
function setResultsPerMarkets(
bytes32[] memory _gameIds,
uint16[] memory _typeIds,
uint24[] memory _playerIds,
int24[][] memory _results
) external;
function isGameCancelled(bytes32 _gameId) external view returns (bool);
function cancelGames(bytes32[] memory _gameIds) external;
function cancelMarkets(
bytes32[] memory _gameIds,
uint16[] memory _typeIds,
uint24[] memory _playerIds,
int24[] memory _lines
) external;
function cancelMarket(bytes32 _gameId, uint16 _typeId, uint24 _playerId, int24 _line) external;
function cancelGame(bytes32 _gameId) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./ISportsAMMV2.sol";
interface ISportsAMMV2RiskManager {
struct TypeCap {
uint typeId;
uint cap;
}
struct CapData {
uint capPerSport;
uint capPerChild;
TypeCap[] capPerType;
}
struct DynamicLiquidityData {
uint cutoffTimePerSport;
uint cutoffDividerPerSport;
}
struct RiskData {
uint sportId;
CapData capData;
uint riskMultiplierPerSport;
DynamicLiquidityData dynamicLiquidityData;
}
enum RiskStatus {
NoRisk,
OutOfLiquidity,
InvalidCombination
}
function minBuyInAmount() external view returns (uint);
function maxTicketSize() external view returns (uint);
function maxSupportedAmount() external view returns (uint);
function maxSupportedOdds() external view returns (uint);
function maxAllowedSystemCombinations() external view returns (uint);
function expiryDuration() external view returns (uint);
function liveTradingPerSportAndTypeEnabled(uint _sportId, uint _typeId) external view returns (bool _enabled);
function calculateCapToBeUsed(
bytes32 _gameId,
uint16 _sportId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _maturity,
bool _isLive
) external view returns (uint cap);
function calculateTotalRiskOnGame(
bytes32 _gameId,
uint16 _sportId,
uint _maturity
) external view returns (uint totalRisk);
function checkRisks(
ISportsAMMV2.TradeData[] memory _tradeData,
uint _buyInAmount,
bool _isLive,
uint8 _systemBetDenominator
) external view returns (ISportsAMMV2RiskManager.RiskStatus riskStatus, bool[] memory isMarketOutOfLiquidity);
function checkLimits(
uint _buyInAmount,
uint _totalQuote,
uint _payout,
uint _expectedPayout,
uint _additionalSlippage,
uint _ticketSize
) external view;
function spentOnGame(bytes32 _gameId) external view returns (uint);
function riskPerMarketTypeAndPosition(
bytes32 _gameId,
uint _typeId,
uint _playerId,
uint _position
) external view returns (int);
function checkAndUpdateRisks(
ISportsAMMV2.TradeData[] memory _tradeData,
uint _buyInAmount,
uint _payout,
bool _isLive,
uint8 _systemBetDenominator,
bool _isSGP
) external;
function verifyMerkleTree(ISportsAMMV2.TradeData memory _marketTradeData, bytes32 _rootPerGame) external pure;
function batchVerifyMerkleTree(
ISportsAMMV2.TradeData[] memory _marketTradeData,
bytes32[] memory _rootPerGame
) external pure;
function isSportIdFuture(uint16 _sportsId) external view returns (bool);
function sgpOnSportIdEnabled(uint16 _sportsId) external view returns (bool);
function getMaxSystemBetPayout(
ISportsAMMV2.TradeData[] memory _tradeData,
uint8 _systemBetDenominator,
uint _buyInAmount,
uint _addedPayoutPercentage
) external view returns (uint systemBetPayout, uint systemBetQuote);
function generateCombinations(uint8 n, uint8 k) external pure returns (uint8[][] memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IProxyBetting.sol";
interface IStakingThalesBettingProxy is IProxyBetting {
function preConfirmLiveTrade(bytes32 requestId, uint _buyInAmount) external;
function confirmLiveTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount) external;
function preConfirmSGPTrade(bytes32 requestId, uint _buyInAmount) external;
function confirmSGPTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library AddressSetLib {
struct AddressSet {
address[] elements;
mapping(address => uint) indices;
}
function contains(AddressSet storage set, address candidate) internal view returns (bool) {
if (set.elements.length == 0) {
return false;
}
uint index = set.indices[candidate];
return index != 0 || set.elements[0] == candidate;
}
function getPage(AddressSet storage set, uint index, uint pageSize) internal view returns (address[] memory) {
// NOTE: This implementation should be converted to slice operators if the compiler is updated to v0.6.0+
uint endIndex = index + pageSize; // The check below that endIndex <= index handles overflow.
// If the page extends past the end of the list, truncate it.
if (endIndex > set.elements.length) {
endIndex = set.elements.length;
}
if (endIndex <= index) {
return new address[](0);
}
uint n = endIndex - index; // We already checked for negative overflow.
address[] memory page = new address[](n);
for (uint i; i < n; i++) {
page[i] = set.elements[i + index];
}
return page;
}
function add(AddressSet storage set, address element) internal {
// Adding to a set is an idempotent operation.
if (!contains(set, element)) {
set.indices[element] = set.elements.length;
set.elements.push(element);
}
}
function remove(AddressSet storage set, address element) internal {
require(contains(set, element), "Element not in set.");
// Replace the removed element with the last element of the list.
uint index = set.indices[element];
uint lastIndex = set.elements.length - 1; // We required that element is in the list, so it is not empty.
if (index != lastIndex) {
// No need to shift the last element if it is the one we want to delete.
address shiftedElement = set.elements[lastIndex];
set.elements[index] = shiftedElement;
set.indices[shiftedElement] = index;
}
set.elements.pop();
delete set.indices[element];
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// Clone of syntetix contract without constructor
contract ProxyOwned {
address public owner;
address public nominatedOwner;
bool private _initialized;
bool private _transferredAtInit;
function setOwner(address _owner) public {
require(_owner != address(0), "Owner address cannot be 0");
require(!_initialized, "Already initialized, use nominateNewOwner");
_initialized = true;
owner = _owner;
emit OwnerChanged(address(0), _owner);
}
function nominateNewOwner(address _owner) external onlyOwner {
nominatedOwner = _owner;
emit OwnerNominated(_owner);
}
function acceptOwnership() external {
require(msg.sender == nominatedOwner, "You must be nominated before you can accept ownership");
emit OwnerChanged(owner, nominatedOwner);
owner = nominatedOwner;
nominatedOwner = address(0);
}
function transferOwnershipAtInit(address proxyAddress) external onlyOwner {
require(proxyAddress != address(0), "Invalid address");
require(!_transferredAtInit, "Already transferred");
owner = proxyAddress;
_transferredAtInit = true;
emit OwnerChanged(owner, proxyAddress);
}
modifier onlyOwner() {
_onlyOwner();
_;
}
function _onlyOwner() private view {
require(msg.sender == owner, "Only the contract owner may perform this action");
}
event OwnerNominated(address newOwner);
event OwnerChanged(address oldOwner, address newOwner);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// Inheritance
import "./ProxyOwned.sol";
// Clone of syntetix contract without constructor
contract ProxyPausable is ProxyOwned {
uint public lastPauseTime;
bool public paused;
/**
* @notice Change the paused state of the contract
* @dev Only the contract owner may call this.
*/
function setPaused(bool _paused) external onlyOwner {
// Ensure we're actually changing the state before we do anything
if (_paused == paused) {
return;
}
// Set our paused state.
paused = _paused;
// If applicable, set the last pause time.
if (paused) {
lastPauseTime = block.timestamp;
}
// Let everyone know that our pause state has changed.
emit PauseChanged(paused);
}
event PauseChanged(bool isPaused);
modifier notPaused() {
require(!paused, "This action cannot be performed while the contract is paused");
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier
* available, which can be aplied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*/
contract ProxyReentrancyGuard {
/// @dev counter to allow mutex lock with only one SSTORE operation
uint256 private _guardCounter;
bool private _initialized;
function initNonReentrant() public {
require(!_initialized, "Already initialized");
_initialized = true;
_guardCounter = 1;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_guardCounter += 1;
uint256 localCounter = _guardCounter;
_;
require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
}
}{
"optimizer": {
"enabled": true,
"runs": 100
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"CallerNotAllowed","type":"error"},{"inputs":[],"name":"DirectionsCannotBeEmpty","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"FreeBetExpired","type":"error"},{"inputs":[],"name":"FreeBetNotExpired","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OnlyCallableFromLiveTradingProcessor","type":"error"},{"inputs":[],"name":"OnlyCallableFromSGPTradingProcessor","type":"error"},{"inputs":[],"name":"OnlyCallableFromSpeedMarketsAMMCreator","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SpeedMarketsAMMCreatorNotSet","type":"error"},{"inputs":[],"name":"UnknownActiveTicket","type":"error"},{"inputs":[],"name":"UnknownLiveTicket","type":"error"},{"inputs":[],"name":"UnknownSGPTicket","type":"error"},{"inputs":[],"name":"UnknownSpeedMarketTicketOwner","type":"error"},{"inputs":[],"name":"UnknownTicket","type":"error"},{"inputs":[],"name":"UnsupportedCollateral","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"bool","name":"supported","type":"bool"},{"indexed":false,"internalType":"address","name":"addressToApprove","type":"address"}],"name":"CollateralSupportChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"timeFrame","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"directionsCount","type":"uint256"}],"name":"FreeBetChainedSpeedMarketTradeRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":false,"internalType":"uint16","name":"legsCount","type":"uint16"}],"name":"FreeBetLiveParlayTradeRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"requestId","type":"bytes32"}],"name":"FreeBetLiveTradeRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"requestId","type":"bytes32"}],"name":"FreeBetSGPTradeRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"speedMarket","type":"address"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"earned","type":"uint256"}],"name":"FreeBetSpeedMarketResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bytes32","name":"requestId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"strikeTime","type":"uint64"},{"indexed":false,"internalType":"enum ISpeedMarketsAMMCreator.Direction","name":"direction","type":"uint8"}],"name":"FreeBetSpeedMarketTradeRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"createdSpeedMarket","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"FreeBetSpeedTrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"ticket","type":"address"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"earned","type":"uint256"}],"name":"FreeBetTicketResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"createdTicket","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"isLive","type":"bool"}],"name":"FreeBetTrade","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","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":"addressManager","type":"address"}],"name":"SetAddressManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"freeBetExpirationPeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"freeBetExpirationUpgrade","type":"uint256"}],"name":"SetFreeBetExpirationPeriod","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liveTradingProcessor","type":"address"}],"name":"SetLiveTradingProcessor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sgpTradingProcessor","type":"address"}],"name":"SetSGPTradingProcessor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sportsAMM","type":"address"}],"name":"SetSportsAMM","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"name":"UpdateMaxApprovalSpeedMarketsAMM","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"funder","type":"address"}],"name":"UserFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"address","name":"_collateral","type":"address"},{"indexed":false,"internalType":"address","name":"_receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"UserFundingRemoved","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"bool","name":"_supported","type":"bool"},{"internalType":"address","name":"addressToApprove","type":"address"}],"name":"addSupportedCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressManager","outputs":[{"internalType":"contract IAddressManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"balancePerUserAndCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"address","name":"_createdTicket","type":"address"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"address","name":"_collateral","type":"address"}],"name":"confirmLiveTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"address","name":"_createdTicket","type":"address"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"address","name":"_collateral","type":"address"}],"name":"confirmSGPTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_resolvedSpeedMarket","type":"address"},{"internalType":"uint256","name":"_exercized","type":"uint256"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"bool","name":"isChained","type":"bool"}],"name":"confirmSpeedMarketResolved","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"address","name":"_createdMarket","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"bool","name":"_isChainedSpeedMarket","type":"bool"}],"name":"confirmSpeedOrChainedSpeedMarketTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_resolvedTicket","type":"address"}],"name":"confirmTicketResolved","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"freeBetExpiration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"freeBetExpirationPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"freeBetExpirationUpgrade","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"fund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_amountPerUser","type":"uint256"}],"name":"fundBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint256","name":"_pageSize","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getActiveChainedSpeedMarketsPerUser","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint256","name":"_pageSize","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getActiveSpeedMarketsPerUser","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint256","name":"_pageSize","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getActiveTicketsPerUser","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint256","name":"_pageSize","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getResolvedChainedSpeedMarketsPerUser","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint256","name":"_pageSize","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getResolvedSpeedMarketsPerUser","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint256","name":"_pageSize","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"getResolvedTicketsPerUser","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint256","name":"_pageSize","type":"uint256"}],"name":"getUsersFreeBetDataPerCollateral","outputs":[{"internalType":"address[]","name":"allUsers","type":"address[]"},{"internalType":"uint256[]","name":"freeBetAmounts","type":"uint256[]"},{"internalType":"bool[]","name":"isValid","type":"bool[]"},{"internalType":"uint256[]","name":"timeToExpiration","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"},{"internalType":"uint256","name":"_pageSize","type":"uint256"}],"name":"getUsersWithFreeBetPerCollateral","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initNonReentrant","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_sportsAMMV2","type":"address"},{"internalType":"address","name":"_liveTradingProcessor","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_collateral","type":"address"}],"name":"isFreeBetValid","outputs":[{"internalType":"bool","name":"isValid","type":"bool"},{"internalType":"uint256","name":"timeToExpiration","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastPauseTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"liveRequestsPerUser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liveTradingProcessor","outputs":[{"internalType":"contract ILiveTradingProcessor","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":[{"internalType":"address","name":"_user","type":"address"}],"name":"numOfActiveChainedSpeedMarketsPerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"numOfActiveSpeedMarketsPerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"numOfActiveTicketsPerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"numOfResolvedChainedSpeedMarketsPerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"numOfResolvedSpeedMarketsPerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"numOfResolvedTicketsPerUser","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_collateral","type":"address"}],"name":"numOfUsersWithFreeBetPerCollateral","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"paidPerTicket","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"},{"internalType":"address","name":"_collateral","type":"address"}],"name":"removeExpiredUserFunding","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"removeUserFunding","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"removeUserFundingBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"retrieveFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addressManager","type":"address"}],"name":"setAddressManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_freeBetExpirationPeriod","type":"uint256"},{"internalType":"uint256","name":"_freeBetExpirationUpgrade","type":"uint256"}],"name":"setFreeBetExpirationPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_liveTradingProcessor","type":"address"}],"name":"setLiveTradingProcessor","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":"address","name":"_sgpTradingProcessor","type":"address"}],"name":"setSGPTradingProcessor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sportsAMM","type":"address"}],"name":"setSportsAMM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_freeBetExpiration","type":"uint256"}],"name":"setUserFreeBetExpiration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"},{"internalType":"address","name":"_collateral","type":"address"}],"name":"setUsersWithAlreadyFundedFreeBetPerCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"sgpRequestsPerUser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sgpTradingProcessor","outputs":[{"internalType":"contract ISGPTradingProcessor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"speedMarketRequestToUser","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sportsAMM","outputs":[{"internalType":"contract ISportsAMMV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supportedCollateral","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ticketToUser","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"}],"name":"trade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint64","name":"timeFrame","type":"uint64"},{"internalType":"uint256","name":"strikePrice","type":"uint256"},{"internalType":"uint256","name":"strikePriceSlippage","type":"uint256"},{"internalType":"enum ISpeedMarketsAMMCreator.Direction[]","name":"directions","type":"uint8[]"},{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint256","name":"buyinAmount","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"}],"internalType":"struct ISpeedMarketsAMMCreator.ChainedSpeedMarketParams","name":"_params","type":"tuple"}],"name":"tradeChainedSpeedMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"_gameId","type":"string"},{"internalType":"uint16","name":"_sportId","type":"uint16"},{"internalType":"uint16","name":"_typeId","type":"uint16"},{"internalType":"int24","name":"_line","type":"int24"},{"internalType":"uint8","name":"_position","type":"uint8"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"},{"internalType":"uint256","name":"_expectedQuote","type":"uint256"},{"internalType":"uint256","name":"_additionalSlippage","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint24","name":"_playerId","type":"uint24"}],"internalType":"struct ILiveTradingProcessor.LiveTradeData","name":"_liveTradeData","type":"tuple"}],"name":"tradeLive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"string","name":"gameId","type":"string"},{"internalType":"uint16","name":"sportId","type":"uint16"},{"internalType":"uint16","name":"typeId","type":"uint16"},{"internalType":"int24","name":"line","type":"int24"},{"internalType":"uint8","name":"position","type":"uint8"},{"internalType":"uint256","name":"expectedLegOdd","type":"uint256"},{"internalType":"uint24","name":"playerId","type":"uint24"}],"internalType":"struct ILiveTradingProcessor.LiveParlayLeg[]","name":"legs","type":"tuple[]"},{"internalType":"uint256","name":"buyInAmount","type":"uint256"},{"internalType":"uint256","name":"expectedPayout","type":"uint256"},{"internalType":"uint256","name":"additionalSlippage","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"address","name":"collateral","type":"address"}],"internalType":"struct ILiveTradingProcessor.LiveParlayTradeData","name":"_liveParlayTradeData","type":"tuple"}],"name":"tradeLiveParlay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"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":"struct ISGPTradingProcessor.SGPTradeData","name":"_sgpTradeData","type":"tuple"}],"name":"tradeSGP","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint64","name":"strikeTime","type":"uint64"},{"internalType":"uint64","name":"delta","type":"uint64"},{"internalType":"uint256","name":"strikePrice","type":"uint256"},{"internalType":"uint256","name":"strikePriceSlippage","type":"uint256"},{"internalType":"enum ISpeedMarketsAMMCreator.Direction","name":"direction","type":"uint8"},{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint256","name":"buyinAmount","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"skewImpact","type":"uint256"}],"internalType":"struct ISpeedMarketsAMMCreator.SpeedMarketParams","name":"_params","type":"tuple"}],"name":"tradeSpeedMarket","outputs":[],"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":"uint256","name":"_expectedQuote","type":"uint256"},{"internalType":"uint256","name":"_additionalSlippage","type":"uint256"},{"internalType":"address","name":"_referrer","type":"address"},{"internalType":"address","name":"_collateral","type":"address"},{"internalType":"uint8","name":"_systemBetDenominator","type":"uint8"}],"name":"tradeSystemBet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"proxyAddress","type":"address"}],"name":"transferOwnershipAtInit","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b50615513806100206000396000f3fe608060405234801561001057600080fd5b50600436106103715760003560e01c80639168fdd1116101d5578063cf6bf6f811610105578063ea7e3518116100a8578063ea7e351814610901578063ebc7977214610914578063edc5de8e1461091c578063efac480d14610925578063f0b9c91514610938578063f38329d914610961578063fa550eba14610984578063fd734898146109a7578063fecc198b146109ba57600080fd5b8063cf6bf6f814610851578063d1e7be9614610864578063d5cb1c5514610877578063dd1d97731461088a578063dd37861d1461089d578063e74e33e0146108b0578063e81e52ee146108db578063e94cdf5f146108ee57600080fd5b8063aba84bb111610178578063aba84bb114610766578063b1b5823b14610790578063be899c8914610799578063bea4ae20146107ac578063c0c53b8b146107bf578063c2002645146107d2578063c3b83f5f146107fb578063c99252881461080e578063cab7bf561461082657600080fd5b80639168fdd1146106ac57806391b4ded9146106d5578063924fbde1146106de57806394bd2313146106f157806397fba4d914610704578063a12d5b071461072d578063a13d6b6a14610740578063a2fabfce1461075357600080fd5b806342d851ed116102b05780635c975abb116102535780635c975abb146105c05780635fd5528c146105dd57806376d9038f1461060657806379ba50971461062f5780637eda2d7b14610637578063884806761461064a5780638da5cb5b1461065d5780638f974cd91461067057806390e3afb21461069957600080fd5b806342d851ed1461051257806344cab7ee1461053b578063485b23bf1461054e5780634a54bcac146105615780634d1ac474146105745780634fa98ca51461058757806352aa17a21461059a57806353a47bb7146105ad57600080fd5b806316c38b3c1161031857806316c38b3c14610457578063188aa45d1461046a57806326f3c6bc1461047d578063275fd2471461049d578063285e51f9146104b05780633a572121146104c35780633a687e2b146104ec5780633ab76e9f146104ff57600080fd5b806304de96c5146103765780630634961a1461039f5780630652b57a146103b4578063084a2fa4146103c757806311e9c08d146103da57806313af4035146104085780631590a4a41461041b5780631627540c14610444575b600080fd5b600654610389906001600160a01b031681565b60405161039691906140d1565b60405180910390f35b6103b26103ad366004614118565b6109e3565b005b6103b26103c2366004614177565b610c42565b6103b26103d5366004614194565b610cc7565b6103fa6103e8366004614177565b600a6020526000908152604090205481565b604051908152602001610396565b6103b2610416366004614177565b610cfb565b6103fa610429366004614177565b6001600160a01b03166000908152600c602052604090205490565b6103b2610452366004614177565b610e0c565b6103b26104653660046141d5565b610e5f565b6103b26104783660046141f2565b610ed1565b61049061048b36600461421e565b610ef1565b60405161039691906142a8565b6103b26104ab3660046142ff565b610f21565b6103b26104be366004614366565b61101b565b6103896104d13660046143a1565b600b602052600090815260409020546001600160a01b031681565b6103b26104fa3660046143ba565b61132f565b601454610389906001600160a01b031681565b6103896105203660046143a1565b600f602052600090815260409020546001600160a01b031681565b600e54610389906001600160a01b031681565b61049061055c36600461421e565b611458565b6103b261056f366004614440565b61147e565b6103b2610582366004614459565b61170a565b6103b26105953660046144a4565b6118b8565b6103b26105a83660046144f6565b61192a565b600154610389906001600160a01b031681565b6003546105cd9060ff1681565b6040519015158152602001610396565b6103896105eb3660046143a1565b6015602052600090815260409020546001600160a01b031681565b6103fa610614366004614177565b6001600160a01b031660009081526018602052604090205490565b6103b2611aeb565b6103b261064536600461452a565b611bc3565b61049061065836600461421e565b611e47565b600054610389906001600160a01b031681565b61038961067e366004614177565b6009602052600090815260409020546001600160a01b031681565b6103b26106a7366004614459565b611e6d565b6103fa6106ba366004614177565b6001600160a01b03166000908152600d602052604090205490565b6103fa60025481565b6104906106ec366004614569565b611ed8565b6103b26106ff366004614177565b611efe565b6103fa610712366004614177565b6001600160a01b031660009081526019602052604090205490565b6103b261073b36600461452a565b611f78565b6103b261074e366004614177565b612017565b61049061076136600461421e565b612288565b61077961077436600461459e565b6122ae565b604080519215158352602083019190915201610396565b6103fa60125481565b6103b26107a7366004614194565b6122c6565b6103b26107ba3660046145d7565b61235e565b6103b26107cd3660046144a4565b612449565b6103fa6107e0366004614177565b6001600160a01b031660009081526016602052604090205490565b6103b2610809366004614177565b612592565b6005546103899061010090046001600160a01b031681565b6103fa61083436600461459e565b600760209081526000928352604080842090915290825290205481565b6103b261085f366004614633565b612682565b6103b26108723660046144f6565b612a44565b6103b2610885366004614684565b612c24565b6103b26108983660046146b4565b612d96565b6104906108ab36600461421e565b612df1565b6103fa6108be36600461459e565b601060209081526000928352604080842090915290825290205481565b6103b26108e9366004614177565b612e17565b6103b26108fc366004614177565b612e96565b61049061090f36600461421e565b612f10565b6103b2612f36565b6103fa60115481565b6103b26109333660046146e7565b612f94565b6103fa610946366004614177565b6001600160a01b031660009081526017602052604090205490565b61097461096f366004614569565b6130bd565b60405161039694939291906147ab565b6105cd610992366004614177565b60086020526000908152604090205460ff1681565b6103b26109b5366004614829565b613333565b6103fa6109c8366004614177565b6001600160a01b031660009081526013602052604090205490565b60145460405163bf40fac160e01b815260206004820152601760248201527629b832b2b226b0b935b2ba39a0a6a6a932b9b7b63b32b960491b60448201526000916001600160a01b03169063bf40fac190606401602060405180830381865afa158015610a54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a789190614864565b9050336001600160a01b03821614610aa35760405163015783e960e51b815260040160405180910390fd5b6001600160a01b038087166000908152600960205260409020541680610adc57604051634f8c20e160e01b815260040160405180910390fd5b6000610aea828689896134e5565b90508315610b7a576001600160a01b0382166000908152601860205260409020610b14908961357f565b610b315760405163e31c3b0360e01b815260040160405180910390fd5b6001600160a01b0382166000908152601860205260409020610b5390896135f7565b6001600160a01b0382166000908152601960205260409020610b759089613750565b610bfd565b6001600160a01b0382166000908152601660205260409020610b9c908961357f565b610bb95760405163e31c3b0360e01b815260040160405180910390fd5b6001600160a01b0382166000908152601660205260409020610bdb90896135f7565b6001600160a01b0382166000908152601760205260409020610bfd9089613750565b7fc89b3148d3afeabb5482b0e35264162df1b695835f82880119e55bcb6a87db46888383604051610c3093929190614881565b60405180910390a15050505050505050565b610c4a6137a2565b6001600160a01b038116610c715760405163e6c4247b60e01b815260040160405180910390fd5b601480546001600160a01b0319166001600160a01b0383161790556040517f954328d28753080b3c499697bde218fd8b53e924669801835383aa346e6940ee90610cbc9083906140d1565b60405180910390a150565b610ccf6137a2565b6001600160a01b0392831660009081526010602090815260408083209490951682529290925291902055565b6001600160a01b038116610d525760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064015b60405180910390fd5b600154600160a01b900460ff1615610dbe5760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610d49565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b03199091161781556040516000805160206154be83398151915291610cbc9184906148a5565b610e146137a2565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290610cbc9083906140d1565b610e676137a2565b60035460ff16151581151514610ece576003805460ff191682151590811790915560ff1615610e9557426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec590602001610cbc565b50565b610ed96137a2565b610eed6001600160a01b0383163383613816565b5050565b6001600160a01b0381166000908152601960205260409020606090610f17908585613873565b90505b9392505050565b60035460ff1615610f445760405162461bcd60e51b8152600401610d49906148bf565b600160046000828254610f579190614932565b9091555050600454610f676137a2565b6001600160a01b03831660009081526008602052604090205460ff16610fa05760405163621a135560e01b815260040160405180910390fd5b60005b84811015610ff2576000868683818110610fbf57610fbf614945565b9050602002016020810190610fd49190614177565b9050610fe181868661398d565b50610feb8161495b565b9050610fa3565b5060045481146110145760405162461bcd60e51b8152600401610d4990614974565b5050505050565b60035460ff161561103e5760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546110519190614932565b90915550506004543361106a60c0840160a08501614177565b6001600160a01b03811660009081526008602052604090205460c08501359060ff166110a95760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156110ef57604051631e9acf1760e31b815260040160405180910390fd5b6110f98383613b32565b6111165760405163b79adbed60e01b815260040160405180910390fd5b60145460405163bf40fac160e01b81526000916001600160a01b03169063bf40fac190611145906004016149ab565b602060405180830381865afa158015611162573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111869190614864565b90506001600160a01b0381166111af5760405163a62e3f5360e01b815260040160405180910390fd5b6111bc60808701876149db565b90506000036111de5760405163f6d764fb60e01b815260040160405180910390fd5b60405163064dbc8760e21b81526000906001600160a01b03831690631936f21c9061120d908a90600401614ab4565b6020604051808303816000875af115801561122c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112509190614b91565b60008181526015602090815260409182902080546001600160a01b031916339081179091559293507f4ad010e4ad2be2d7a9851444a5bc66965d9e5c19e9956c1b780a3493efaa3ce19291849160c08c0135918c35916112b4918e01908e01614baa565b6112c160808e018e6149db565b604080516001600160a01b03909816885260208801969096529486019390935260608501919091526001600160401b031660808401525060a082015260c0015b60405180910390a150505050506004548114610eed5760405162461bcd60e51b8152600401610d4990614974565b60035460ff16156113525760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546113659190614932565b90915550506004546001600160a01b03821660009081526008602052604090205433908390889060ff166113ac5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156113f257604051631e9acf1760e31b815260040160405180910390fd5b6113fc8383613b32565b6114195760405163b79adbed60e01b815260040160405180910390fd5b61142a8b8b8b8b8b8b8b6000613ba9565b505050600454811461144e5760405162461bcd60e51b8152600401610d4990614974565b5050505050505050565b6001600160a01b0381166000908152600d60205260409020606090610f17908585613873565b60035460ff16156114a15760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546114b49190614932565b9091555050600454336114cd60e0840160c08501614177565b6001600160a01b03811660009081526008602052604090205460e08501359060ff1661150c5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481111561155257604051631e9acf1760e31b815260040160405180910390fd5b61155c8383613b32565b6115795760405163b79adbed60e01b815260040160405180910390fd5b60145460405163bf40fac160e01b81526000916001600160a01b03169063bf40fac1906115a8906004016149ab565b602060405180830381865afa1580156115c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e99190614864565b90506001600160a01b0381166116125760405163a62e3f5360e01b815260040160405180910390fd5b604051636032a02760e01b81526000906001600160a01b03831690636032a02790611641908a90600401614bc5565b6020604051808303816000875af1158015611660573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116849190614b91565b60008181526015602090815260409182902080546001600160a01b031916339081179091559293507f16b6809ad60f6976d5779745fe0a2d8bd4de64c93c39e8b200cbe1316393dd2b9291849160e08c0135918c35916116e8918e01908e01614baa565b6116f860c08e0160a08f01614c81565b60405161130196959493929190614c9c565b60035460ff161561172d5760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546117409190614932565b909155505060045460005b8381101561189057600085858381811061176757611767614945565b905060200201602081019061177c9190614177565b6001600160a01b038082166000908152600760209081526040808320938916835292905220549091501561187f576001600160a01b038082166000908152601060209081526040808320938816835292905220541580159061180257506001600160a01b0380821660009081526010602090815260408083209388168352929052205442115b8061184957506001600160a01b038082166000908152601060209081526040808320938816835292905220541580156118495750426011546012546118479190614932565b105b61186657604051631c8b0d0d60e31b815260040160405180910390fd5b60005461187f90829086906001600160a01b031661398d565b506118898161495b565b905061174b565b5060045481146118b25760405162461bcd60e51b8152600401610d4990614974565b50505050565b60035460ff16156118db5760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546118ee9190614932565b90915550506004546118fe6137a2565b61190984848461398d565b60045481146118b25760405162461bcd60e51b8152600401610d4990614974565b60035460ff161561194d5760405162461bcd60e51b8152600401610d49906148bf565b3361195e60c0830160a08401614177565b6001600160a01b038116600090815260086020908152604090912054908401359060ff1661199f5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156119e557604051631e9acf1760e31b815260040160405180910390fd5b6119ef8383613b32565b611a0c5760405163b79adbed60e01b815260040160405180910390fd5b600e546040516306fea14960e21b81526000916001600160a01b031690631bfa852490611a3d908890600401614faf565b6020604051808303816000875af1158015611a5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a809190614b91565b6000818152600f602090815260409182902080546001600160a01b0319163390811790915591519293507f2a107e8d4cba0aa559543eb234520741dac54e7db15d8a96afc8df0dcb8f752592611adc9291890135908590615035565b60405180910390a15050505050565b6001546001600160a01b03163314611b635760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610d49565b6000546001546040516000805160206154be83398151915292611b94926001600160a01b03918216929116906148a5565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b60035460ff1615611be65760405162461bcd60e51b8152600401610d49906148bf565b600160046000828254611bf99190614932565b9091555050600454600e546001600160a01b03163314611c2c5760405163b0a51f3b60e01b815260040160405180910390fd5b6000858152600f60205260409020546001600160a01b031680611c6257604051630b8efbb360e31b815260040160405180910390fd5b6001600160a01b038316611cea57600560019054906101000a90046001600160a01b03166001600160a01b031663aeb0f1646040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ce79190614864565b92505b6001600160a01b03831660009081526008602052604090205460ff16611d235760405163621a135560e01b815260040160405180910390fd5b6001600160a01b03808216600090815260076020908152604080832093871683529290522054841115611d6957604051631e9acf1760e31b815260040160405180910390fd5b6001600160a01b03808216600090815260076020908152604080832093871683529290529081208054869290611da0908490615056565b90915550506001600160a01b03858116600090815260096020908152604080832080546001600160a01b0319169486169485179055928252600c905220611de79086613750565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838585836001604051611e1d9493929190615069565b60405180910390a15060045481146110145760405162461bcd60e51b8152600401610d4990614974565b6001600160a01b0381166000908152601860205260409020606090610f17908585613873565b611e756137a2565b60005b828110156118b257611ec8848483818110611e9557611e95614945565b9050602002016020810190611eaa9190614177565b6001600160a01b038416600090815260136020526040902090613750565b611ed18161495b565b9050611e78565b6001600160a01b0383166000908152601360205260409020606090610f17908484613873565b611f066137a2565b6001600160a01b038116611f2d5760405163e6c4247b60e01b815260040160405180910390fd5b600680546001600160a01b0319166001600160a01b0383161790556040517f8b5670b3bc5c520b7a3580c753ba88065a561ad52a99674196859b044ea2028590610cbc9083906140d1565b60035460ff1615611f9b5760405162461bcd60e51b8152600401610d49906148bf565b600160046000828254611fae9190614932565b90915550506004546006546001600160a01b03163314611fe15760405163bae55a2d60e01b815260040160405180910390fd5b6000858152600b60205260409020546001600160a01b031680611c6257604051637692dc6960e01b815260040160405180910390fd5b60055461010090046001600160a01b031633146120475760405163015783e960e51b815260040160405180910390fd5b6001600160a01b03808216600090815260096020526040902054168061208057604051634f8c20e160e01b815260040160405180910390fd5b6001600160a01b0381166000908152600c602052604090206120a2908361357f565b6120bf5760405163e31c3b0360e01b815260040160405180910390fd5b600080836001600160a01b031663242a8a6b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612100573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121249190614b91565b90506000846001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015612166573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218a9190614864565b90506000856001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121f09190614b91565b90506121fe858385846134e5565b6001600160a01b0386166000908152600c6020526040902090945061222390876135f7565b6001600160a01b0385166000908152600d602052604090206122459087613750565b7f66c0cec01a971dcd7f2bb22f7cc4b244c8f9ee6deb5540b073d0f10b4539e8b586868660405161227893929190614881565b60405180910390a1505050505050565b6001600160a01b0381166000908152601660205260409020606090610f17908585613873565b6000806122bb8484613d8e565b909590945092505050565b60035460ff16156122e95760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546122fc9190614932565b90915550506004546001600160a01b03831660009081526008602052604090205460ff1661233d5760405163621a135560e01b815260040160405180910390fd5b6123526001600160a01b038416333085613e71565b61190984848433613e99565b60035460ff16156123815760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546123949190614932565b90915550506004546001600160a01b03831660009081526008602052604090205460ff166123d55760405163621a135560e01b815260040160405180910390fd5b6123f633306123e48786615093565b6001600160a01b038716929190613e71565b60005b84811015610ff257600086868381811061241557612415614945565b905060200201602081019061242a9190614177565b905061243881868633613e99565b506124428161495b565b90506123f9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b031660008115801561248e5750825b90506000826001600160401b031660011480156124aa5750303b155b9050811580156124b8575080155b156124d65760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561250057845460ff60401b1916600160401b1785555b61250988610cfb565b612511612f36565b60058054610100600160a81b0319166101006001600160a01b038a81169190910291909117909155600680546001600160a01b031916918816919091179055831561144e57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610c30565b61259a6137a2565b6001600160a01b0381166125e25760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610d49565b600154600160a81b900460ff16156126325760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610d49565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b1790556040516000805160206154be83398151915291610cbc9184906148a5565b60035460ff16156126a55760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546126b89190614932565b90915550506004805460145460405163bf40fac160e01b815291926000926001600160a01b039092169163bf40fac1916126f291016149ab565b602060405180830381865afa15801561270f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127339190614864565b9050336001600160a01b0382161461275e57604051632c58be7f60e11b815260040160405180910390fd5b6001600160a01b0385166128615760145460405163bf40fac160e01b815260206004820152600f60248201526e53706565644d61726b657473414d4d60881b60448201526000916001600160a01b03169063bf40fac190606401602060405180830381865afa1580156127d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f99190614864565b9050806001600160a01b0316639324cac76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612839573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285d9190614864565b9550505b6001600160a01b03851660009081526008602052604090205460ff1661289a5760405163621a135560e01b815260040160405180910390fd5b6000878152601560205260409020546001600160a01b0316806128d05760405163d287b7b160e01b815260040160405180910390fd5b6001600160a01b038082166000908152600760209081526040808320938a168352929052205485111561291657604051631e9acf1760e31b815260040160405180910390fd5b6001600160a01b038082166000908152600760209081526040808320938a168352929052908120805487929061294d908490615056565b90915550506001600160a01b03878116600090815260096020526040902080546001600160a01b03191691831691909117905583156129ad576001600160a01b03811660009081526018602052604090206129a89088613750565b6129cf565b6001600160a01b03811660009081526016602052604090206129cf9088613750565b604080516001600160a01b0389811682526020820188905283168183015290517fbb19d9066294aca9bb81670382b302c69053c26acb490cfbb522b07ac2bb0b8b9181900360600190a150506004548114612a3c5760405162461bcd60e51b8152600401610d4990614974565b505050505050565b60035460ff1615612a675760405162461bcd60e51b8152600401610d49906148bf565b33612a7860c0830160a08401614177565b6001600160a01b038116600090815260086020908152604090912054908401359060ff16612ab95760405163621a135560e01b815260040160405180910390fd5b6001600160a01b03808416600090815260076020908152604080832093861683529290522054811115612aff57604051631e9acf1760e31b815260040160405180910390fd5b612b098383613b32565b612b265760405163b79adbed60e01b815260040160405180910390fd5b600654604051630b797ad560e31b81526000916001600160a01b031690635bcbd6a890612b57908890600401615118565b6020604051808303816000875af1158015612b76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9a9190614b91565b6000818152600b6020908152604090912080546001600160a01b031916339081179091559192507f13521780bb9b565e5bebd38ee1b8785a00883d3f9b61a2891e0d1ff9a88a9a3f919087013583612bf289806149db565b604080516001600160a01b0390961686526020860194909452928401919091525061ffff166060820152608001611adc565b612c2c6137a2565b6001600160a01b0383166000908152600860205260409020805460ff19168315801591909117909155612cd25760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390612c899084906000199060040161526e565b6020604051808303816000875af1158015612ca8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ccc9190615287565b50612d46565b60405163095ea7b360e01b81526001600160a01b0384169063095ea7b390612d0190849060009060040161526e565b6020604051808303816000875af1158015612d20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d449190615287565b505b604080516001600160a01b038581168252841515602083015283168183015290517f6faac0235cc96062eedf703b47b00eaa4680c2cfa3ffae5865f699137264f0159181900360600190a1505050565b612d9e6137a2565b60118290558015612daf5780612db1565b425b60125560408051838152602081018390527fddd57316dedc944a54a2d4ccbec17883e25f23eebb11ea1a4800f36502a469f0910160405180910390a15050565b6001600160a01b0381166000908152600c60205260409020606090610f17908585613873565b612e1f6137a2565b6001600160a01b038116612e465760405163e6c4247b60e01b815260040160405180910390fd5b60058054610100600160a81b0319166101006001600160a01b038416021790556040517f9985022676a73860c32a3b91ea7a7dfe2d5e87c148f50eb519d8b0f33ab7f8b990610cbc9083906140d1565b612e9e6137a2565b6001600160a01b038116612ec55760405163e6c4247b60e01b815260040160405180910390fd5b600e80546001600160a01b0319166001600160a01b0383161790556040517f63bb8d8b29c197c8b58de8e513ab743bce7bf84c87adc05f2cc18e609d5d0e8690610cbc9083906140d1565b6001600160a01b0381166000908152601760205260409020606090610f17908585613873565b60055460ff1615612f7f5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b6044820152606401610d49565b6005805460ff19166001908117909155600455565b60035460ff1615612fb75760405162461bcd60e51b8152600401610d49906148bf565b600160046000828254612fca9190614932565b90915550506004546001600160a01b03831660009081526008602052604090205433908490899060ff166130115760405163621a135560e01b815260040160405180910390fd5b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481111561305757604051631e9acf1760e31b815260040160405180910390fd5b6130618383613b32565b61307e5760405163b79adbed60e01b815260040160405180910390fd5b61308e8c8c8c8c8c8c8c8c613ba9565b50505060045481146130b25760405162461bcd60e51b8152600401610d4990614974565b505050505050505050565b6001600160a01b038316600090815260136020526040902054606090819081908190851115613102576001600160a01b03871660009081526013602052604090205494505b846001600160401b0381111561311a5761311a6152a4565b604051908082528060200260200182016040528015613143578160200160208202803683370190505b509350846001600160401b0381111561315e5761315e6152a4565b604051908082528060200260200182016040528015613187578160200160208202803683370190505b509150846001600160401b038111156131a2576131a26152a4565b6040519080825280602002602001820160405280156131cb578160200160208202803683370190505b509250846001600160401b038111156131e6576131e66152a4565b60405190808252806020026020018201604052801561320f578160200160208202803683370190505b50905060005b85811015613329576001600160a01b038816600090815260136020526040812061323f838a614932565b8154811061324f5761324f614945565b6000918252602090912001546001600160a01b03169050613270818a613d8e565b85848151811061328257613282614945565b6020026020010185858151811061329b5761329b614945565b602002602001018281525082151515158152505050808683815181106132c3576132c3614945565b6001600160a01b039283166020918202929092018101919091528282166000908152600782526040808220938d168252929091522054855186908490811061330d5761330d614945565b6020908102919091010152506133228161495b565b9050613215565b5093509350935093565b60035460ff16156133565760405162461bcd60e51b8152600401610d49906148bf565b3361336961014083016101208401614177565b6001600160a01b03811660009081526008602052604090205460a08401359060ff166133a85760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156133ee57604051631e9acf1760e31b815260040160405180910390fd5b6133f88383613b32565b6134155760405163b79adbed60e01b815260040160405180910390fd5b60065460405163584ceab960e11b81526000916001600160a01b03169063b099d572906134469088906004016152ba565b6020604051808303816000875af1158015613465573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134899190614b91565b6000818152600b60205260409081902080546001600160a01b0319163390811790915590519192507ff226e2c0a5c137f1fc5331d8dabe76ea9810e121bc9b18737125bcaa884daa8091611adc919060a0890135908590615035565b60008215613577578183111561353a5760005461350f906001600160a01b03868116911684613816565b6135198284615056565b90508015613535576135356001600160a01b0385168683613816565b613577565b6001600160a01b03808616600090815260076020908152604080832093881683529290529081208054859290613571908490614932565b90915550505b949350505050565b81546000908103613592575060006135f1565b6001600160a01b0382166000908152600184016020526040902054801515806135ed5750826001600160a01b0316846000016000815481106135d6576135d6614945565b6000918252602090912001546001600160a01b0316145b9150505b92915050565b613601828261357f565b6136435760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b6044820152606401610d49565b6001600160a01b038116600090815260018084016020526040822054845490929161366d91615056565b90508082146136f957600084600001828154811061368d5761368d614945565b60009182526020909120015485546001600160a01b03909116915081908690859081106136bc576136bc614945565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b835484908061370a5761370a6153cb565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b61375a828261357f565b610eed5781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555050565b6000546001600160a01b031633146138145760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610d49565b565b61386e83846001600160a01b031663a9059cbb858560405160240161383c92919061526e565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613f79565b505050565b606060006138818385614932565b8554909150811115613891575083545b8381116138ae575050604080516000815260208101909152610f1a565b60006138ba8583615056565b90506000816001600160401b038111156138d6576138d66152a4565b6040519080825280602002602001820160405280156138ff578160200160208202803683370190505b50905060005b8281101561398257876139188883614932565b8154811061392857613928614945565b9060005260206000200160009054906101000a90046001600160a01b031682828151811061395857613958614945565b6001600160a01b03909216602092830291909101909101528061397a8161495b565b915050613905565b509695505050505050565b6001600160a01b03821660009081526008602052604090205460ff166139c65760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616808452939091528082205490516370a0823160e01b81529092906370a0823190613a139030906004016140d1565b602060405180830381865afa158015613a30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a549190614b91565b9050600082118015613a665750818110155b15613a7f57613a7f6001600160a01b0385168484613816565b6001600160a01b03808616600090815260076020908152604080832093881683529281528282208290556013905220613ab8908661357f565b15613adf576001600160a01b0384166000908152601360205260409020613adf90866135f7565b604080516001600160a01b0380881682528087166020830152851691810191909152606081018390527f5aecd162bb2e2f8d70294fe483d6a8596cf8555a650f08a962220feaba5c21fe90608001611adc565b6001600160a01b038083166000908152601060209081526040808320938516835292905290812054421080610f1a57506001600160a01b03808416600090815260106020908152604080832093861683529290522054158015610f1a575042601154601254613ba19190614932565b119392505050565b3360009081526007602090815260408083206001600160a01b038616845290915281208054889290613bdc908490615056565b909155506000905060ff821615613c7c576005546040516349d8615760e11b81526101009091046001600160a01b0316906393b0c2ae90613c32908c908c908c908c908c908c908c906000908d906004016153e1565b6020604051808303816000875af1158015613c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c759190614864565b9050613d04565b6005546040516214e17b60e41b81526101009091046001600160a01b03169063014e17b090613cbe908c908c908c908c908c908c908c9060009060040161543d565b6020604051808303816000875af1158015613cdd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d019190614864565b90505b6001600160a01b038116600090815260096020908152604080832080546001600160a01b031916339081179091558352600c9091529020613d459082613750565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838188336000604051613d7b9493929190615069565b60405180910390a1505050505050505050565b6001600160a01b038116600090815260086020526040812054819060ff168015613ddc57506001600160a01b0380851660009081526007602090815260408083209387168352929052205415155b15613e6a576001600160a01b038085166000908152601060209081526040808320938716835292905290812054613e2257601154601254613e1d9190614932565b613e49565b6001600160a01b038086166000908152601060209081526040808320938816835292905220545b9050428111925082613e5c576000613e66565b613e664282615056565b9150505b9250929050565b6118b284856001600160a01b03166323b872dd86868660405160240161383c93929190614881565b6001600160a01b0383166000908152601360205260409020613ebb9085613750565b6001600160a01b03808516600090815260076020908152604080832093871683529290529081208054849290613ef2908490614932565b9091555050601154613f049042614932565b6001600160a01b03858116600081815260106020908152604080832089861680855290835292819020959095558451928352820152918201849052821660608201527ff462a3a00cd477c2aa6252dfbff27a7cf55ad6f75c526f4650b5bb92ef2912689060800160405180910390a150505050565b6000613f8e6001600160a01b03841683613fd3565b90508051600014158015613fb3575080806020019051810190613fb19190615287565b155b1561386e5782604051635274afe760e01b8152600401610d4991906140d1565b6060610f1a8383600084600080856001600160a01b03168486604051613ff9919061548e565b60006040518083038185875af1925050503d8060008114614036576040519150601f19603f3d011682016040523d82523d6000602084013e61403b565b606091505b509150915061404b868383614055565b9695505050505050565b60608261406a57614065826140a8565b610f1a565b815115801561408157506001600160a01b0384163b155b156140a15783604051639996b31560e01b8152600401610d4991906140d1565b5080610f1a565b8051156140b85780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0391909116815260200190565b6001600160a01b0381168114610ece57600080fd5b8035614105816140e5565b919050565b8015158114610ece57600080fd5b600080600080600060a0868803121561413057600080fd5b853561413b816140e5565b945060208601359350604086013592506060860135614159816140e5565b915060808601356141698161410a565b809150509295509295909350565b60006020828403121561418957600080fd5b8135610f1a816140e5565b6000806000606084860312156141a957600080fd5b83356141b4816140e5565b925060208401356141c4816140e5565b929592945050506040919091013590565b6000602082840312156141e757600080fd5b8135610f1a8161410a565b6000806040838503121561420557600080fd5b8235614210816140e5565b946020939093013593505050565b60008060006060848603121561423357600080fd5b8335925060208401359150604084013561424c816140e5565b809150509250925092565b6001600160a01b03169052565b600081518084526020808501945080840160005b8381101561429d5781516001600160a01b031687529582019590820190600101614278565b509495945050505050565b602081526000610f1a6020830184614264565b60008083601f8401126142cd57600080fd5b5081356001600160401b038111156142e457600080fd5b6020830191508360208260051b8501011115613e6a57600080fd5b6000806000806060858703121561431557600080fd5b84356001600160401b0381111561432b57600080fd5b614337878288016142bb565b909550935050602085013561434b816140e5565b9150604085013561435b816140e5565b939692955090935050565b60006020828403121561437857600080fd5b81356001600160401b0381111561438e57600080fd5b82016101008185031215610f1a57600080fd5b6000602082840312156143b357600080fd5b5035919050565b600080600080600080600060c0888a0312156143d557600080fd5b87356001600160401b038111156143eb57600080fd5b6143f78a828b016142bb565b9098509650506020880135945060408801359350606088013592506080880135614420816140e5565b915060a0880135614430816140e5565b8091505092959891949750929550565b6000610140828403121561445357600080fd5b50919050565b60008060006040848603121561446e57600080fd5b83356001600160401b0381111561448457600080fd5b614490868287016142bb565b909450925050602084013561424c816140e5565b6000806000606084860312156144b957600080fd5b83356144c4816140e5565b925060208401356144d4816140e5565b9150604084013561424c816140e5565b600060c0828403121561445357600080fd5b60006020828403121561450857600080fd5b81356001600160401b0381111561451e57600080fd5b6135ed848285016144e4565b6000806000806080858703121561454057600080fd5b843593506020850135614552816140e5565b925060408501359150606085013561435b816140e5565b60008060006060848603121561457e57600080fd5b8335614589816140e5565b95602085013595506040909401359392505050565b600080604083850312156145b157600080fd5b82356145bc816140e5565b915060208301356145cc816140e5565b809150509250929050565b600080600080606085870312156145ed57600080fd5b84356001600160401b0381111561460357600080fd5b61460f878288016142bb565b9095509350506020850135614623816140e5565b9396929550929360400135925050565b600080600080600060a0868803121561464b57600080fd5b85359450602086013561465d816140e5565b9350604086013561466d816140e5565b92506060860135915060808601356141698161410a565b60008060006060848603121561469957600080fd5b83356146a4816140e5565b925060208401356144d48161410a565b600080604083850312156146c757600080fd5b50508035926020909101359150565b803560ff8116811461410557600080fd5b60008060008060008060008060e0898b03121561470357600080fd5b88356001600160401b0381111561471957600080fd5b6147258b828c016142bb565b909950975050602089013595506040890135945060608901359350608089013561474e816140e5565b925060a089013561475e816140e5565b915061476c60c08a016146d6565b90509295985092959890939650565b600081518084526020808501945080840160005b8381101561429d5781518752958201959082019060010161478f565b6080815260006147be6080830187614264565b6020838203818501526147d1828861477b565b8481036040860152865180825282880193509082019060005b818110156148085784511515835293830193918301916001016147ea565b5050848103606086015261481c818761477b565b9998505050505050505050565b60006020828403121561483b57600080fd5b81356001600160401b0381111561485157600080fd5b82016101608185031215610f1a57600080fd5b60006020828403121561487657600080fd5b8151610f1a816140e5565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0392831681529116602082015260400190565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b634e487b7160e01b600052601160045260246000fd5b808201808211156135f1576135f161491c565b634e487b7160e01b600052603260045260246000fd5b60006001820161496d5761496d61491c565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526016908201527529b832b2b226b0b935b2ba39a0a6a6a1b932b0ba37b960511b604082015260600190565b6000808335601e198436030181126149f257600080fd5b8301803591506001600160401b03821115614a0c57600080fd5b6020019150600581901b3603821315613e6a57600080fd5b80356001600160401b038116811461410557600080fd5b6000808335601e19843603018112614a5257600080fd5b83016020810192503590506001600160401b03811115614a7157600080fd5b8060051b3603821315613e6a57600080fd5b60028110614aa157634e487b7160e01b600052602160045260246000fd5b9052565b80356002811061410557600080fd5b6000602080835261012083018435828501526001600160401b03614ad9838701614a24565b1660408501526040850135606085015260608501356080850152614b006080860186614a3b565b61010060a087018190529281905290916000919061014087015b82841015614b4657614b3481614b2f87614aa5565b614a83565b93850193600193909301928501614b1a565b614b5260a08a016140fa565b9550614b6160c0890187614257565b60c089013560e0890152614b7760e08a016140fa565b9550614b8582890187614257565b98975050505050505050565b600060208284031215614ba357600080fd5b5051919050565b600060208284031215614bbc57600080fd5b610f1a82614a24565b813581526101408101614bda60208401614a24565b6001600160401b03166020830152614bf460408401614a24565b6001600160401b0381166040840152506060830135606083015260808301356080830152614c2460a08401614aa5565b614c3160a0840182614a83565b50614c3e60c084016140fa565b614c4b60c0840182614257565b5060e083013560e0830152610100614c648185016140fa565b614c7082850182614257565b505061012092830135919092015290565b600060208284031215614c9357600080fd5b610f1a82614aa5565b6001600160a01b03871681526020810186905260408101859052606081018490526001600160401b038316608082015260c08101614cdd60a0830184614a83565b979650505050505050565b803561ffff8116811461410557600080fd5b8035600281900b811461410557600080fd5b803562ffffff8116811461410557600080fd5b81835260006001600160fb1b03831115614d3857600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b8581101561429d5761ffff614d7583614ce8565b16875260ff614d858484016146d6565b16838801526040614d97818401614cfa565b60020b908801526060968701969190910190600101614d61565b81835260006020808501808196508560051b810191508460005b87811015614e385782840389528135601e19883603018112614dec57600080fd5b870185810190356001600160401b03811115614e0757600080fd5b606081023603821315614e1957600080fd5b614e24868284614d51565b9a87019a9550505090840190600101614dcb565b5091979650505050505050565b6000823561015e19833603018112614e5c57600080fd5b90910192915050565b81835260006020808501808196508560051b810191508460005b87811015614e38578284038952614e968288614e45565b61016081358652614ea8878301614ce8565b61ffff16878701526040614ebd838201614ce8565b61ffff1690870152606082810135908701526080614edc8184016146d6565b60ff169087015260a0614ef0838201614cfa565b614efe8289018260020b9052565b505060c0614f0d818401614d0c565b62ffffff169087015260e0614f2483820184614a3b565b83838a0152614f36848a018284614d1f565b9350505050610100614f4a81840184614a3b565b888403838a0152614f5c848284614d1f565b9350505050610120614f6f8184016146d6565b60ff1690870152610140614f8583820184614a3b565b935087830382890152614f99838583614db1565b9c89019c97505050928601925050600101614e7f565b602081526000614fbf8384614a3b565b60c06020850152614fd460e085018284614e65565b9150506020840135604084015260408401356060840152606084013560808401526080840135615003816140e5565b6001600160a01b0390811660a08581019190915285013590615024826140e5565b1660c0939093019290925250919050565b6001600160a01b039390931683526020830191909152604082015260600190565b818103818111156135f1576135f161491c565b6001600160a01b039485168152602081019390935292166040820152901515606082015260800190565b80820281158282048414176135f1576135f161491c565b6000808335601e198436030181126150c157600080fd5b83016020810192503590506001600160401b038111156150e057600080fd5b803603821315613e6a57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000602080835260e080840161512e8687614a3b565b60c085880181905292819052610100600582901b8801810193908801836000805b85811015615218578b880360ff1901845282353688900360de19018112615174578283fd5b870161518081806150aa565b8b8b526151908c8c0182846150ef565b91505061519e8c8301614ce8565b61ffff9081168b8e0152604090806151b7858401614ce8565b16828d0152505060606151cb818401614cfa565b60020b908b015260806151df8382016146d6565b60ff16908b015260a082810135908b01526151fb878301614d0c565b62ffffff169987019990995250928901929189019160010161514f565b50505050858901356040890152604089013560608901526060890135608089015261524560808a016140fa565b955061525460a0890187614257565b61526060a08a016140fa565b9550614e3881890187614257565b6001600160a01b03929092168252602082015260400190565b60006020828403121561529957600080fd5b8151610f1a8161410a565b634e487b7160e01b600052604160045260246000fd5b6020815260006152ca83846150aa565b6101608060208601526152e2610180860183856150ef565b92506152f060208701614ce8565b61ffff81166040870152915061530860408701614ce8565b61ffff81166060870152915061532060608701614cfa565b9150615331608086018360020b9052565b61533d608087016146d6565b60ff811660a0870152915060a086013560c086015260c086013560e0860152610100915060e0860135828601526153758287016140fa565b915061012061538681870184614257565b6153918188016140fa565b9250506101406153a381870184614257565b6153ae818801614d0c565b9250506153c18186018362ffffff169052565b5090949350505050565b634e487b7160e01b600052603160045260246000fd5b60006101008083526153f68184018c8e614e65565b602084019a909a525050604081019690965260608601949094526001600160a01b039283166080860152911660a0840152151560c083015260ff1660e09091015292915050565b60e08152600061545160e083018a8c614e65565b602083019890985250604081019590955260608501939093526001600160a01b0391821660808501521660a0830152151560c09091015292915050565b6000825160005b818110156154af5760208186018101518583015201615495565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca264697066735822122038cbc93f707dd349f7da3f43653a7fb98a396351228f8807f57dab762c9b675164736f6c63430008140033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103715760003560e01c80639168fdd1116101d5578063cf6bf6f811610105578063ea7e3518116100a8578063ea7e351814610901578063ebc7977214610914578063edc5de8e1461091c578063efac480d14610925578063f0b9c91514610938578063f38329d914610961578063fa550eba14610984578063fd734898146109a7578063fecc198b146109ba57600080fd5b8063cf6bf6f814610851578063d1e7be9614610864578063d5cb1c5514610877578063dd1d97731461088a578063dd37861d1461089d578063e74e33e0146108b0578063e81e52ee146108db578063e94cdf5f146108ee57600080fd5b8063aba84bb111610178578063aba84bb114610766578063b1b5823b14610790578063be899c8914610799578063bea4ae20146107ac578063c0c53b8b146107bf578063c2002645146107d2578063c3b83f5f146107fb578063c99252881461080e578063cab7bf561461082657600080fd5b80639168fdd1146106ac57806391b4ded9146106d5578063924fbde1146106de57806394bd2313146106f157806397fba4d914610704578063a12d5b071461072d578063a13d6b6a14610740578063a2fabfce1461075357600080fd5b806342d851ed116102b05780635c975abb116102535780635c975abb146105c05780635fd5528c146105dd57806376d9038f1461060657806379ba50971461062f5780637eda2d7b14610637578063884806761461064a5780638da5cb5b1461065d5780638f974cd91461067057806390e3afb21461069957600080fd5b806342d851ed1461051257806344cab7ee1461053b578063485b23bf1461054e5780634a54bcac146105615780634d1ac474146105745780634fa98ca51461058757806352aa17a21461059a57806353a47bb7146105ad57600080fd5b806316c38b3c1161031857806316c38b3c14610457578063188aa45d1461046a57806326f3c6bc1461047d578063275fd2471461049d578063285e51f9146104b05780633a572121146104c35780633a687e2b146104ec5780633ab76e9f146104ff57600080fd5b806304de96c5146103765780630634961a1461039f5780630652b57a146103b4578063084a2fa4146103c757806311e9c08d146103da57806313af4035146104085780631590a4a41461041b5780631627540c14610444575b600080fd5b600654610389906001600160a01b031681565b60405161039691906140d1565b60405180910390f35b6103b26103ad366004614118565b6109e3565b005b6103b26103c2366004614177565b610c42565b6103b26103d5366004614194565b610cc7565b6103fa6103e8366004614177565b600a6020526000908152604090205481565b604051908152602001610396565b6103b2610416366004614177565b610cfb565b6103fa610429366004614177565b6001600160a01b03166000908152600c602052604090205490565b6103b2610452366004614177565b610e0c565b6103b26104653660046141d5565b610e5f565b6103b26104783660046141f2565b610ed1565b61049061048b36600461421e565b610ef1565b60405161039691906142a8565b6103b26104ab3660046142ff565b610f21565b6103b26104be366004614366565b61101b565b6103896104d13660046143a1565b600b602052600090815260409020546001600160a01b031681565b6103b26104fa3660046143ba565b61132f565b601454610389906001600160a01b031681565b6103896105203660046143a1565b600f602052600090815260409020546001600160a01b031681565b600e54610389906001600160a01b031681565b61049061055c36600461421e565b611458565b6103b261056f366004614440565b61147e565b6103b2610582366004614459565b61170a565b6103b26105953660046144a4565b6118b8565b6103b26105a83660046144f6565b61192a565b600154610389906001600160a01b031681565b6003546105cd9060ff1681565b6040519015158152602001610396565b6103896105eb3660046143a1565b6015602052600090815260409020546001600160a01b031681565b6103fa610614366004614177565b6001600160a01b031660009081526018602052604090205490565b6103b2611aeb565b6103b261064536600461452a565b611bc3565b61049061065836600461421e565b611e47565b600054610389906001600160a01b031681565b61038961067e366004614177565b6009602052600090815260409020546001600160a01b031681565b6103b26106a7366004614459565b611e6d565b6103fa6106ba366004614177565b6001600160a01b03166000908152600d602052604090205490565b6103fa60025481565b6104906106ec366004614569565b611ed8565b6103b26106ff366004614177565b611efe565b6103fa610712366004614177565b6001600160a01b031660009081526019602052604090205490565b6103b261073b36600461452a565b611f78565b6103b261074e366004614177565b612017565b61049061076136600461421e565b612288565b61077961077436600461459e565b6122ae565b604080519215158352602083019190915201610396565b6103fa60125481565b6103b26107a7366004614194565b6122c6565b6103b26107ba3660046145d7565b61235e565b6103b26107cd3660046144a4565b612449565b6103fa6107e0366004614177565b6001600160a01b031660009081526016602052604090205490565b6103b2610809366004614177565b612592565b6005546103899061010090046001600160a01b031681565b6103fa61083436600461459e565b600760209081526000928352604080842090915290825290205481565b6103b261085f366004614633565b612682565b6103b26108723660046144f6565b612a44565b6103b2610885366004614684565b612c24565b6103b26108983660046146b4565b612d96565b6104906108ab36600461421e565b612df1565b6103fa6108be36600461459e565b601060209081526000928352604080842090915290825290205481565b6103b26108e9366004614177565b612e17565b6103b26108fc366004614177565b612e96565b61049061090f36600461421e565b612f10565b6103b2612f36565b6103fa60115481565b6103b26109333660046146e7565b612f94565b6103fa610946366004614177565b6001600160a01b031660009081526017602052604090205490565b61097461096f366004614569565b6130bd565b60405161039694939291906147ab565b6105cd610992366004614177565b60086020526000908152604090205460ff1681565b6103b26109b5366004614829565b613333565b6103fa6109c8366004614177565b6001600160a01b031660009081526013602052604090205490565b60145460405163bf40fac160e01b815260206004820152601760248201527629b832b2b226b0b935b2ba39a0a6a6a932b9b7b63b32b960491b60448201526000916001600160a01b03169063bf40fac190606401602060405180830381865afa158015610a54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a789190614864565b9050336001600160a01b03821614610aa35760405163015783e960e51b815260040160405180910390fd5b6001600160a01b038087166000908152600960205260409020541680610adc57604051634f8c20e160e01b815260040160405180910390fd5b6000610aea828689896134e5565b90508315610b7a576001600160a01b0382166000908152601860205260409020610b14908961357f565b610b315760405163e31c3b0360e01b815260040160405180910390fd5b6001600160a01b0382166000908152601860205260409020610b5390896135f7565b6001600160a01b0382166000908152601960205260409020610b759089613750565b610bfd565b6001600160a01b0382166000908152601660205260409020610b9c908961357f565b610bb95760405163e31c3b0360e01b815260040160405180910390fd5b6001600160a01b0382166000908152601660205260409020610bdb90896135f7565b6001600160a01b0382166000908152601760205260409020610bfd9089613750565b7fc89b3148d3afeabb5482b0e35264162df1b695835f82880119e55bcb6a87db46888383604051610c3093929190614881565b60405180910390a15050505050505050565b610c4a6137a2565b6001600160a01b038116610c715760405163e6c4247b60e01b815260040160405180910390fd5b601480546001600160a01b0319166001600160a01b0383161790556040517f954328d28753080b3c499697bde218fd8b53e924669801835383aa346e6940ee90610cbc9083906140d1565b60405180910390a150565b610ccf6137a2565b6001600160a01b0392831660009081526010602090815260408083209490951682529290925291902055565b6001600160a01b038116610d525760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064015b60405180910390fd5b600154600160a01b900460ff1615610dbe5760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610d49565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b03199091161781556040516000805160206154be83398151915291610cbc9184906148a5565b610e146137a2565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290610cbc9083906140d1565b610e676137a2565b60035460ff16151581151514610ece576003805460ff191682151590811790915560ff1615610e9557426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec590602001610cbc565b50565b610ed96137a2565b610eed6001600160a01b0383163383613816565b5050565b6001600160a01b0381166000908152601960205260409020606090610f17908585613873565b90505b9392505050565b60035460ff1615610f445760405162461bcd60e51b8152600401610d49906148bf565b600160046000828254610f579190614932565b9091555050600454610f676137a2565b6001600160a01b03831660009081526008602052604090205460ff16610fa05760405163621a135560e01b815260040160405180910390fd5b60005b84811015610ff2576000868683818110610fbf57610fbf614945565b9050602002016020810190610fd49190614177565b9050610fe181868661398d565b50610feb8161495b565b9050610fa3565b5060045481146110145760405162461bcd60e51b8152600401610d4990614974565b5050505050565b60035460ff161561103e5760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546110519190614932565b90915550506004543361106a60c0840160a08501614177565b6001600160a01b03811660009081526008602052604090205460c08501359060ff166110a95760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156110ef57604051631e9acf1760e31b815260040160405180910390fd5b6110f98383613b32565b6111165760405163b79adbed60e01b815260040160405180910390fd5b60145460405163bf40fac160e01b81526000916001600160a01b03169063bf40fac190611145906004016149ab565b602060405180830381865afa158015611162573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111869190614864565b90506001600160a01b0381166111af5760405163a62e3f5360e01b815260040160405180910390fd5b6111bc60808701876149db565b90506000036111de5760405163f6d764fb60e01b815260040160405180910390fd5b60405163064dbc8760e21b81526000906001600160a01b03831690631936f21c9061120d908a90600401614ab4565b6020604051808303816000875af115801561122c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112509190614b91565b60008181526015602090815260409182902080546001600160a01b031916339081179091559293507f4ad010e4ad2be2d7a9851444a5bc66965d9e5c19e9956c1b780a3493efaa3ce19291849160c08c0135918c35916112b4918e01908e01614baa565b6112c160808e018e6149db565b604080516001600160a01b03909816885260208801969096529486019390935260608501919091526001600160401b031660808401525060a082015260c0015b60405180910390a150505050506004548114610eed5760405162461bcd60e51b8152600401610d4990614974565b60035460ff16156113525760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546113659190614932565b90915550506004546001600160a01b03821660009081526008602052604090205433908390889060ff166113ac5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156113f257604051631e9acf1760e31b815260040160405180910390fd5b6113fc8383613b32565b6114195760405163b79adbed60e01b815260040160405180910390fd5b61142a8b8b8b8b8b8b8b6000613ba9565b505050600454811461144e5760405162461bcd60e51b8152600401610d4990614974565b5050505050505050565b6001600160a01b0381166000908152600d60205260409020606090610f17908585613873565b60035460ff16156114a15760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546114b49190614932565b9091555050600454336114cd60e0840160c08501614177565b6001600160a01b03811660009081526008602052604090205460e08501359060ff1661150c5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481111561155257604051631e9acf1760e31b815260040160405180910390fd5b61155c8383613b32565b6115795760405163b79adbed60e01b815260040160405180910390fd5b60145460405163bf40fac160e01b81526000916001600160a01b03169063bf40fac1906115a8906004016149ab565b602060405180830381865afa1580156115c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e99190614864565b90506001600160a01b0381166116125760405163a62e3f5360e01b815260040160405180910390fd5b604051636032a02760e01b81526000906001600160a01b03831690636032a02790611641908a90600401614bc5565b6020604051808303816000875af1158015611660573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116849190614b91565b60008181526015602090815260409182902080546001600160a01b031916339081179091559293507f16b6809ad60f6976d5779745fe0a2d8bd4de64c93c39e8b200cbe1316393dd2b9291849160e08c0135918c35916116e8918e01908e01614baa565b6116f860c08e0160a08f01614c81565b60405161130196959493929190614c9c565b60035460ff161561172d5760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546117409190614932565b909155505060045460005b8381101561189057600085858381811061176757611767614945565b905060200201602081019061177c9190614177565b6001600160a01b038082166000908152600760209081526040808320938916835292905220549091501561187f576001600160a01b038082166000908152601060209081526040808320938816835292905220541580159061180257506001600160a01b0380821660009081526010602090815260408083209388168352929052205442115b8061184957506001600160a01b038082166000908152601060209081526040808320938816835292905220541580156118495750426011546012546118479190614932565b105b61186657604051631c8b0d0d60e31b815260040160405180910390fd5b60005461187f90829086906001600160a01b031661398d565b506118898161495b565b905061174b565b5060045481146118b25760405162461bcd60e51b8152600401610d4990614974565b50505050565b60035460ff16156118db5760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546118ee9190614932565b90915550506004546118fe6137a2565b61190984848461398d565b60045481146118b25760405162461bcd60e51b8152600401610d4990614974565b60035460ff161561194d5760405162461bcd60e51b8152600401610d49906148bf565b3361195e60c0830160a08401614177565b6001600160a01b038116600090815260086020908152604090912054908401359060ff1661199f5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156119e557604051631e9acf1760e31b815260040160405180910390fd5b6119ef8383613b32565b611a0c5760405163b79adbed60e01b815260040160405180910390fd5b600e546040516306fea14960e21b81526000916001600160a01b031690631bfa852490611a3d908890600401614faf565b6020604051808303816000875af1158015611a5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a809190614b91565b6000818152600f602090815260409182902080546001600160a01b0319163390811790915591519293507f2a107e8d4cba0aa559543eb234520741dac54e7db15d8a96afc8df0dcb8f752592611adc9291890135908590615035565b60405180910390a15050505050565b6001546001600160a01b03163314611b635760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610d49565b6000546001546040516000805160206154be83398151915292611b94926001600160a01b03918216929116906148a5565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b60035460ff1615611be65760405162461bcd60e51b8152600401610d49906148bf565b600160046000828254611bf99190614932565b9091555050600454600e546001600160a01b03163314611c2c5760405163b0a51f3b60e01b815260040160405180910390fd5b6000858152600f60205260409020546001600160a01b031680611c6257604051630b8efbb360e31b815260040160405180910390fd5b6001600160a01b038316611cea57600560019054906101000a90046001600160a01b03166001600160a01b031663aeb0f1646040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ce79190614864565b92505b6001600160a01b03831660009081526008602052604090205460ff16611d235760405163621a135560e01b815260040160405180910390fd5b6001600160a01b03808216600090815260076020908152604080832093871683529290522054841115611d6957604051631e9acf1760e31b815260040160405180910390fd5b6001600160a01b03808216600090815260076020908152604080832093871683529290529081208054869290611da0908490615056565b90915550506001600160a01b03858116600090815260096020908152604080832080546001600160a01b0319169486169485179055928252600c905220611de79086613750565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838585836001604051611e1d9493929190615069565b60405180910390a15060045481146110145760405162461bcd60e51b8152600401610d4990614974565b6001600160a01b0381166000908152601860205260409020606090610f17908585613873565b611e756137a2565b60005b828110156118b257611ec8848483818110611e9557611e95614945565b9050602002016020810190611eaa9190614177565b6001600160a01b038416600090815260136020526040902090613750565b611ed18161495b565b9050611e78565b6001600160a01b0383166000908152601360205260409020606090610f17908484613873565b611f066137a2565b6001600160a01b038116611f2d5760405163e6c4247b60e01b815260040160405180910390fd5b600680546001600160a01b0319166001600160a01b0383161790556040517f8b5670b3bc5c520b7a3580c753ba88065a561ad52a99674196859b044ea2028590610cbc9083906140d1565b60035460ff1615611f9b5760405162461bcd60e51b8152600401610d49906148bf565b600160046000828254611fae9190614932565b90915550506004546006546001600160a01b03163314611fe15760405163bae55a2d60e01b815260040160405180910390fd5b6000858152600b60205260409020546001600160a01b031680611c6257604051637692dc6960e01b815260040160405180910390fd5b60055461010090046001600160a01b031633146120475760405163015783e960e51b815260040160405180910390fd5b6001600160a01b03808216600090815260096020526040902054168061208057604051634f8c20e160e01b815260040160405180910390fd5b6001600160a01b0381166000908152600c602052604090206120a2908361357f565b6120bf5760405163e31c3b0360e01b815260040160405180910390fd5b600080836001600160a01b031663242a8a6b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612100573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121249190614b91565b90506000846001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015612166573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061218a9190614864565b90506000856001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121f09190614b91565b90506121fe858385846134e5565b6001600160a01b0386166000908152600c6020526040902090945061222390876135f7565b6001600160a01b0385166000908152600d602052604090206122459087613750565b7f66c0cec01a971dcd7f2bb22f7cc4b244c8f9ee6deb5540b073d0f10b4539e8b586868660405161227893929190614881565b60405180910390a1505050505050565b6001600160a01b0381166000908152601660205260409020606090610f17908585613873565b6000806122bb8484613d8e565b909590945092505050565b60035460ff16156122e95760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546122fc9190614932565b90915550506004546001600160a01b03831660009081526008602052604090205460ff1661233d5760405163621a135560e01b815260040160405180910390fd5b6123526001600160a01b038416333085613e71565b61190984848433613e99565b60035460ff16156123815760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546123949190614932565b90915550506004546001600160a01b03831660009081526008602052604090205460ff166123d55760405163621a135560e01b815260040160405180910390fd5b6123f633306123e48786615093565b6001600160a01b038716929190613e71565b60005b84811015610ff257600086868381811061241557612415614945565b905060200201602081019061242a9190614177565b905061243881868633613e99565b506124428161495b565b90506123f9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b031660008115801561248e5750825b90506000826001600160401b031660011480156124aa5750303b155b9050811580156124b8575080155b156124d65760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561250057845460ff60401b1916600160401b1785555b61250988610cfb565b612511612f36565b60058054610100600160a81b0319166101006001600160a01b038a81169190910291909117909155600680546001600160a01b031916918816919091179055831561144e57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610c30565b61259a6137a2565b6001600160a01b0381166125e25760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610d49565b600154600160a81b900460ff16156126325760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610d49565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b1790556040516000805160206154be83398151915291610cbc9184906148a5565b60035460ff16156126a55760405162461bcd60e51b8152600401610d49906148bf565b6001600460008282546126b89190614932565b90915550506004805460145460405163bf40fac160e01b815291926000926001600160a01b039092169163bf40fac1916126f291016149ab565b602060405180830381865afa15801561270f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127339190614864565b9050336001600160a01b0382161461275e57604051632c58be7f60e11b815260040160405180910390fd5b6001600160a01b0385166128615760145460405163bf40fac160e01b815260206004820152600f60248201526e53706565644d61726b657473414d4d60881b60448201526000916001600160a01b03169063bf40fac190606401602060405180830381865afa1580156127d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127f99190614864565b9050806001600160a01b0316639324cac76040518163ffffffff1660e01b8152600401602060405180830381865afa158015612839573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285d9190614864565b9550505b6001600160a01b03851660009081526008602052604090205460ff1661289a5760405163621a135560e01b815260040160405180910390fd5b6000878152601560205260409020546001600160a01b0316806128d05760405163d287b7b160e01b815260040160405180910390fd5b6001600160a01b038082166000908152600760209081526040808320938a168352929052205485111561291657604051631e9acf1760e31b815260040160405180910390fd5b6001600160a01b038082166000908152600760209081526040808320938a168352929052908120805487929061294d908490615056565b90915550506001600160a01b03878116600090815260096020526040902080546001600160a01b03191691831691909117905583156129ad576001600160a01b03811660009081526018602052604090206129a89088613750565b6129cf565b6001600160a01b03811660009081526016602052604090206129cf9088613750565b604080516001600160a01b0389811682526020820188905283168183015290517fbb19d9066294aca9bb81670382b302c69053c26acb490cfbb522b07ac2bb0b8b9181900360600190a150506004548114612a3c5760405162461bcd60e51b8152600401610d4990614974565b505050505050565b60035460ff1615612a675760405162461bcd60e51b8152600401610d49906148bf565b33612a7860c0830160a08401614177565b6001600160a01b038116600090815260086020908152604090912054908401359060ff16612ab95760405163621a135560e01b815260040160405180910390fd5b6001600160a01b03808416600090815260076020908152604080832093861683529290522054811115612aff57604051631e9acf1760e31b815260040160405180910390fd5b612b098383613b32565b612b265760405163b79adbed60e01b815260040160405180910390fd5b600654604051630b797ad560e31b81526000916001600160a01b031690635bcbd6a890612b57908890600401615118565b6020604051808303816000875af1158015612b76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9a9190614b91565b6000818152600b6020908152604090912080546001600160a01b031916339081179091559192507f13521780bb9b565e5bebd38ee1b8785a00883d3f9b61a2891e0d1ff9a88a9a3f919087013583612bf289806149db565b604080516001600160a01b0390961686526020860194909452928401919091525061ffff166060820152608001611adc565b612c2c6137a2565b6001600160a01b0383166000908152600860205260409020805460ff19168315801591909117909155612cd25760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390612c899084906000199060040161526e565b6020604051808303816000875af1158015612ca8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ccc9190615287565b50612d46565b60405163095ea7b360e01b81526001600160a01b0384169063095ea7b390612d0190849060009060040161526e565b6020604051808303816000875af1158015612d20573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d449190615287565b505b604080516001600160a01b038581168252841515602083015283168183015290517f6faac0235cc96062eedf703b47b00eaa4680c2cfa3ffae5865f699137264f0159181900360600190a1505050565b612d9e6137a2565b60118290558015612daf5780612db1565b425b60125560408051838152602081018390527fddd57316dedc944a54a2d4ccbec17883e25f23eebb11ea1a4800f36502a469f0910160405180910390a15050565b6001600160a01b0381166000908152600c60205260409020606090610f17908585613873565b612e1f6137a2565b6001600160a01b038116612e465760405163e6c4247b60e01b815260040160405180910390fd5b60058054610100600160a81b0319166101006001600160a01b038416021790556040517f9985022676a73860c32a3b91ea7a7dfe2d5e87c148f50eb519d8b0f33ab7f8b990610cbc9083906140d1565b612e9e6137a2565b6001600160a01b038116612ec55760405163e6c4247b60e01b815260040160405180910390fd5b600e80546001600160a01b0319166001600160a01b0383161790556040517f63bb8d8b29c197c8b58de8e513ab743bce7bf84c87adc05f2cc18e609d5d0e8690610cbc9083906140d1565b6001600160a01b0381166000908152601760205260409020606090610f17908585613873565b60055460ff1615612f7f5760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b6044820152606401610d49565b6005805460ff19166001908117909155600455565b60035460ff1615612fb75760405162461bcd60e51b8152600401610d49906148bf565b600160046000828254612fca9190614932565b90915550506004546001600160a01b03831660009081526008602052604090205433908490899060ff166130115760405163621a135560e01b815260040160405180910390fd5b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481111561305757604051631e9acf1760e31b815260040160405180910390fd5b6130618383613b32565b61307e5760405163b79adbed60e01b815260040160405180910390fd5b61308e8c8c8c8c8c8c8c8c613ba9565b50505060045481146130b25760405162461bcd60e51b8152600401610d4990614974565b505050505050505050565b6001600160a01b038316600090815260136020526040902054606090819081908190851115613102576001600160a01b03871660009081526013602052604090205494505b846001600160401b0381111561311a5761311a6152a4565b604051908082528060200260200182016040528015613143578160200160208202803683370190505b509350846001600160401b0381111561315e5761315e6152a4565b604051908082528060200260200182016040528015613187578160200160208202803683370190505b509150846001600160401b038111156131a2576131a26152a4565b6040519080825280602002602001820160405280156131cb578160200160208202803683370190505b509250846001600160401b038111156131e6576131e66152a4565b60405190808252806020026020018201604052801561320f578160200160208202803683370190505b50905060005b85811015613329576001600160a01b038816600090815260136020526040812061323f838a614932565b8154811061324f5761324f614945565b6000918252602090912001546001600160a01b03169050613270818a613d8e565b85848151811061328257613282614945565b6020026020010185858151811061329b5761329b614945565b602002602001018281525082151515158152505050808683815181106132c3576132c3614945565b6001600160a01b039283166020918202929092018101919091528282166000908152600782526040808220938d168252929091522054855186908490811061330d5761330d614945565b6020908102919091010152506133228161495b565b9050613215565b5093509350935093565b60035460ff16156133565760405162461bcd60e51b8152600401610d49906148bf565b3361336961014083016101208401614177565b6001600160a01b03811660009081526008602052604090205460a08401359060ff166133a85760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156133ee57604051631e9acf1760e31b815260040160405180910390fd5b6133f88383613b32565b6134155760405163b79adbed60e01b815260040160405180910390fd5b60065460405163584ceab960e11b81526000916001600160a01b03169063b099d572906134469088906004016152ba565b6020604051808303816000875af1158015613465573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134899190614b91565b6000818152600b60205260409081902080546001600160a01b0319163390811790915590519192507ff226e2c0a5c137f1fc5331d8dabe76ea9810e121bc9b18737125bcaa884daa8091611adc919060a0890135908590615035565b60008215613577578183111561353a5760005461350f906001600160a01b03868116911684613816565b6135198284615056565b90508015613535576135356001600160a01b0385168683613816565b613577565b6001600160a01b03808616600090815260076020908152604080832093881683529290529081208054859290613571908490614932565b90915550505b949350505050565b81546000908103613592575060006135f1565b6001600160a01b0382166000908152600184016020526040902054801515806135ed5750826001600160a01b0316846000016000815481106135d6576135d6614945565b6000918252602090912001546001600160a01b0316145b9150505b92915050565b613601828261357f565b6136435760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b6044820152606401610d49565b6001600160a01b038116600090815260018084016020526040822054845490929161366d91615056565b90508082146136f957600084600001828154811061368d5761368d614945565b60009182526020909120015485546001600160a01b03909116915081908690859081106136bc576136bc614945565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b835484908061370a5761370a6153cb565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b61375a828261357f565b610eed5781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555050565b6000546001600160a01b031633146138145760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610d49565b565b61386e83846001600160a01b031663a9059cbb858560405160240161383c92919061526e565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613f79565b505050565b606060006138818385614932565b8554909150811115613891575083545b8381116138ae575050604080516000815260208101909152610f1a565b60006138ba8583615056565b90506000816001600160401b038111156138d6576138d66152a4565b6040519080825280602002602001820160405280156138ff578160200160208202803683370190505b50905060005b8281101561398257876139188883614932565b8154811061392857613928614945565b9060005260206000200160009054906101000a90046001600160a01b031682828151811061395857613958614945565b6001600160a01b03909216602092830291909101909101528061397a8161495b565b915050613905565b509695505050505050565b6001600160a01b03821660009081526008602052604090205460ff166139c65760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616808452939091528082205490516370a0823160e01b81529092906370a0823190613a139030906004016140d1565b602060405180830381865afa158015613a30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a549190614b91565b9050600082118015613a665750818110155b15613a7f57613a7f6001600160a01b0385168484613816565b6001600160a01b03808616600090815260076020908152604080832093881683529281528282208290556013905220613ab8908661357f565b15613adf576001600160a01b0384166000908152601360205260409020613adf90866135f7565b604080516001600160a01b0380881682528087166020830152851691810191909152606081018390527f5aecd162bb2e2f8d70294fe483d6a8596cf8555a650f08a962220feaba5c21fe90608001611adc565b6001600160a01b038083166000908152601060209081526040808320938516835292905290812054421080610f1a57506001600160a01b03808416600090815260106020908152604080832093861683529290522054158015610f1a575042601154601254613ba19190614932565b119392505050565b3360009081526007602090815260408083206001600160a01b038616845290915281208054889290613bdc908490615056565b909155506000905060ff821615613c7c576005546040516349d8615760e11b81526101009091046001600160a01b0316906393b0c2ae90613c32908c908c908c908c908c908c908c906000908d906004016153e1565b6020604051808303816000875af1158015613c51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c759190614864565b9050613d04565b6005546040516214e17b60e41b81526101009091046001600160a01b03169063014e17b090613cbe908c908c908c908c908c908c908c9060009060040161543d565b6020604051808303816000875af1158015613cdd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d019190614864565b90505b6001600160a01b038116600090815260096020908152604080832080546001600160a01b031916339081179091558352600c9091529020613d459082613750565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838188336000604051613d7b9493929190615069565b60405180910390a1505050505050505050565b6001600160a01b038116600090815260086020526040812054819060ff168015613ddc57506001600160a01b0380851660009081526007602090815260408083209387168352929052205415155b15613e6a576001600160a01b038085166000908152601060209081526040808320938716835292905290812054613e2257601154601254613e1d9190614932565b613e49565b6001600160a01b038086166000908152601060209081526040808320938816835292905220545b9050428111925082613e5c576000613e66565b613e664282615056565b9150505b9250929050565b6118b284856001600160a01b03166323b872dd86868660405160240161383c93929190614881565b6001600160a01b0383166000908152601360205260409020613ebb9085613750565b6001600160a01b03808516600090815260076020908152604080832093871683529290529081208054849290613ef2908490614932565b9091555050601154613f049042614932565b6001600160a01b03858116600081815260106020908152604080832089861680855290835292819020959095558451928352820152918201849052821660608201527ff462a3a00cd477c2aa6252dfbff27a7cf55ad6f75c526f4650b5bb92ef2912689060800160405180910390a150505050565b6000613f8e6001600160a01b03841683613fd3565b90508051600014158015613fb3575080806020019051810190613fb19190615287565b155b1561386e5782604051635274afe760e01b8152600401610d4991906140d1565b6060610f1a8383600084600080856001600160a01b03168486604051613ff9919061548e565b60006040518083038185875af1925050503d8060008114614036576040519150601f19603f3d011682016040523d82523d6000602084013e61403b565b606091505b509150915061404b868383614055565b9695505050505050565b60608261406a57614065826140a8565b610f1a565b815115801561408157506001600160a01b0384163b155b156140a15783604051639996b31560e01b8152600401610d4991906140d1565b5080610f1a565b8051156140b85780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0391909116815260200190565b6001600160a01b0381168114610ece57600080fd5b8035614105816140e5565b919050565b8015158114610ece57600080fd5b600080600080600060a0868803121561413057600080fd5b853561413b816140e5565b945060208601359350604086013592506060860135614159816140e5565b915060808601356141698161410a565b809150509295509295909350565b60006020828403121561418957600080fd5b8135610f1a816140e5565b6000806000606084860312156141a957600080fd5b83356141b4816140e5565b925060208401356141c4816140e5565b929592945050506040919091013590565b6000602082840312156141e757600080fd5b8135610f1a8161410a565b6000806040838503121561420557600080fd5b8235614210816140e5565b946020939093013593505050565b60008060006060848603121561423357600080fd5b8335925060208401359150604084013561424c816140e5565b809150509250925092565b6001600160a01b03169052565b600081518084526020808501945080840160005b8381101561429d5781516001600160a01b031687529582019590820190600101614278565b509495945050505050565b602081526000610f1a6020830184614264565b60008083601f8401126142cd57600080fd5b5081356001600160401b038111156142e457600080fd5b6020830191508360208260051b8501011115613e6a57600080fd5b6000806000806060858703121561431557600080fd5b84356001600160401b0381111561432b57600080fd5b614337878288016142bb565b909550935050602085013561434b816140e5565b9150604085013561435b816140e5565b939692955090935050565b60006020828403121561437857600080fd5b81356001600160401b0381111561438e57600080fd5b82016101008185031215610f1a57600080fd5b6000602082840312156143b357600080fd5b5035919050565b600080600080600080600060c0888a0312156143d557600080fd5b87356001600160401b038111156143eb57600080fd5b6143f78a828b016142bb565b9098509650506020880135945060408801359350606088013592506080880135614420816140e5565b915060a0880135614430816140e5565b8091505092959891949750929550565b6000610140828403121561445357600080fd5b50919050565b60008060006040848603121561446e57600080fd5b83356001600160401b0381111561448457600080fd5b614490868287016142bb565b909450925050602084013561424c816140e5565b6000806000606084860312156144b957600080fd5b83356144c4816140e5565b925060208401356144d4816140e5565b9150604084013561424c816140e5565b600060c0828403121561445357600080fd5b60006020828403121561450857600080fd5b81356001600160401b0381111561451e57600080fd5b6135ed848285016144e4565b6000806000806080858703121561454057600080fd5b843593506020850135614552816140e5565b925060408501359150606085013561435b816140e5565b60008060006060848603121561457e57600080fd5b8335614589816140e5565b95602085013595506040909401359392505050565b600080604083850312156145b157600080fd5b82356145bc816140e5565b915060208301356145cc816140e5565b809150509250929050565b600080600080606085870312156145ed57600080fd5b84356001600160401b0381111561460357600080fd5b61460f878288016142bb565b9095509350506020850135614623816140e5565b9396929550929360400135925050565b600080600080600060a0868803121561464b57600080fd5b85359450602086013561465d816140e5565b9350604086013561466d816140e5565b92506060860135915060808601356141698161410a565b60008060006060848603121561469957600080fd5b83356146a4816140e5565b925060208401356144d48161410a565b600080604083850312156146c757600080fd5b50508035926020909101359150565b803560ff8116811461410557600080fd5b60008060008060008060008060e0898b03121561470357600080fd5b88356001600160401b0381111561471957600080fd5b6147258b828c016142bb565b909950975050602089013595506040890135945060608901359350608089013561474e816140e5565b925060a089013561475e816140e5565b915061476c60c08a016146d6565b90509295985092959890939650565b600081518084526020808501945080840160005b8381101561429d5781518752958201959082019060010161478f565b6080815260006147be6080830187614264565b6020838203818501526147d1828861477b565b8481036040860152865180825282880193509082019060005b818110156148085784511515835293830193918301916001016147ea565b5050848103606086015261481c818761477b565b9998505050505050505050565b60006020828403121561483b57600080fd5b81356001600160401b0381111561485157600080fd5b82016101608185031215610f1a57600080fd5b60006020828403121561487657600080fd5b8151610f1a816140e5565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0392831681529116602082015260400190565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b634e487b7160e01b600052601160045260246000fd5b808201808211156135f1576135f161491c565b634e487b7160e01b600052603260045260246000fd5b60006001820161496d5761496d61491c565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526016908201527529b832b2b226b0b935b2ba39a0a6a6a1b932b0ba37b960511b604082015260600190565b6000808335601e198436030181126149f257600080fd5b8301803591506001600160401b03821115614a0c57600080fd5b6020019150600581901b3603821315613e6a57600080fd5b80356001600160401b038116811461410557600080fd5b6000808335601e19843603018112614a5257600080fd5b83016020810192503590506001600160401b03811115614a7157600080fd5b8060051b3603821315613e6a57600080fd5b60028110614aa157634e487b7160e01b600052602160045260246000fd5b9052565b80356002811061410557600080fd5b6000602080835261012083018435828501526001600160401b03614ad9838701614a24565b1660408501526040850135606085015260608501356080850152614b006080860186614a3b565b61010060a087018190529281905290916000919061014087015b82841015614b4657614b3481614b2f87614aa5565b614a83565b93850193600193909301928501614b1a565b614b5260a08a016140fa565b9550614b6160c0890187614257565b60c089013560e0890152614b7760e08a016140fa565b9550614b8582890187614257565b98975050505050505050565b600060208284031215614ba357600080fd5b5051919050565b600060208284031215614bbc57600080fd5b610f1a82614a24565b813581526101408101614bda60208401614a24565b6001600160401b03166020830152614bf460408401614a24565b6001600160401b0381166040840152506060830135606083015260808301356080830152614c2460a08401614aa5565b614c3160a0840182614a83565b50614c3e60c084016140fa565b614c4b60c0840182614257565b5060e083013560e0830152610100614c648185016140fa565b614c7082850182614257565b505061012092830135919092015290565b600060208284031215614c9357600080fd5b610f1a82614aa5565b6001600160a01b03871681526020810186905260408101859052606081018490526001600160401b038316608082015260c08101614cdd60a0830184614a83565b979650505050505050565b803561ffff8116811461410557600080fd5b8035600281900b811461410557600080fd5b803562ffffff8116811461410557600080fd5b81835260006001600160fb1b03831115614d3857600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b8581101561429d5761ffff614d7583614ce8565b16875260ff614d858484016146d6565b16838801526040614d97818401614cfa565b60020b908801526060968701969190910190600101614d61565b81835260006020808501808196508560051b810191508460005b87811015614e385782840389528135601e19883603018112614dec57600080fd5b870185810190356001600160401b03811115614e0757600080fd5b606081023603821315614e1957600080fd5b614e24868284614d51565b9a87019a9550505090840190600101614dcb565b5091979650505050505050565b6000823561015e19833603018112614e5c57600080fd5b90910192915050565b81835260006020808501808196508560051b810191508460005b87811015614e38578284038952614e968288614e45565b61016081358652614ea8878301614ce8565b61ffff16878701526040614ebd838201614ce8565b61ffff1690870152606082810135908701526080614edc8184016146d6565b60ff169087015260a0614ef0838201614cfa565b614efe8289018260020b9052565b505060c0614f0d818401614d0c565b62ffffff169087015260e0614f2483820184614a3b565b83838a0152614f36848a018284614d1f565b9350505050610100614f4a81840184614a3b565b888403838a0152614f5c848284614d1f565b9350505050610120614f6f8184016146d6565b60ff1690870152610140614f8583820184614a3b565b935087830382890152614f99838583614db1565b9c89019c97505050928601925050600101614e7f565b602081526000614fbf8384614a3b565b60c06020850152614fd460e085018284614e65565b9150506020840135604084015260408401356060840152606084013560808401526080840135615003816140e5565b6001600160a01b0390811660a08581019190915285013590615024826140e5565b1660c0939093019290925250919050565b6001600160a01b039390931683526020830191909152604082015260600190565b818103818111156135f1576135f161491c565b6001600160a01b039485168152602081019390935292166040820152901515606082015260800190565b80820281158282048414176135f1576135f161491c565b6000808335601e198436030181126150c157600080fd5b83016020810192503590506001600160401b038111156150e057600080fd5b803603821315613e6a57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6000602080835260e080840161512e8687614a3b565b60c085880181905292819052610100600582901b8801810193908801836000805b85811015615218578b880360ff1901845282353688900360de19018112615174578283fd5b870161518081806150aa565b8b8b526151908c8c0182846150ef565b91505061519e8c8301614ce8565b61ffff9081168b8e0152604090806151b7858401614ce8565b16828d0152505060606151cb818401614cfa565b60020b908b015260806151df8382016146d6565b60ff16908b015260a082810135908b01526151fb878301614d0c565b62ffffff169987019990995250928901929189019160010161514f565b50505050858901356040890152604089013560608901526060890135608089015261524560808a016140fa565b955061525460a0890187614257565b61526060a08a016140fa565b9550614e3881890187614257565b6001600160a01b03929092168252602082015260400190565b60006020828403121561529957600080fd5b8151610f1a8161410a565b634e487b7160e01b600052604160045260246000fd5b6020815260006152ca83846150aa565b6101608060208601526152e2610180860183856150ef565b92506152f060208701614ce8565b61ffff81166040870152915061530860408701614ce8565b61ffff81166060870152915061532060608701614cfa565b9150615331608086018360020b9052565b61533d608087016146d6565b60ff811660a0870152915060a086013560c086015260c086013560e0860152610100915060e0860135828601526153758287016140fa565b915061012061538681870184614257565b6153918188016140fa565b9250506101406153a381870184614257565b6153ae818801614d0c565b9250506153c18186018362ffffff169052565b5090949350505050565b634e487b7160e01b600052603160045260246000fd5b60006101008083526153f68184018c8e614e65565b602084019a909a525050604081019690965260608601949094526001600160a01b039283166080860152911660a0840152151560c083015260ff1660e09091015292915050565b60e08152600061545160e083018a8c614e65565b602083019890985250604081019590955260608501939093526001600160a01b0391821660808501521660a0830152151560c09091015292915050565b6000825160005b818110156154af5760208186018101518583015201615495565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca264697066735822122038cbc93f707dd349f7da3f43653a7fb98a396351228f8807f57dab762c9b675164736f6c63430008140033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.