Source Code
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 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 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; // 🆕 added for player props
}
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 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 fulfillLiveTrade(bytes32 _requestId, bool allow, uint approvedAmount) external;
function requestLiveTrade(LiveTradeData calldata _liveTradeData) external returns (bytes32);
}// 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"}],"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":"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
608060405234801561001057600080fd5b506151b9806100206000396000f3fe608060405234801561001057600080fd5b50600436106103665760003560e01c80639168fdd1116101ca578063cab7bf5611610105578063ea7e3518116100a8578063ea7e3518146108e3578063ebc79772146108f6578063edc5de8e146108fe578063efac480d14610907578063f0b9c9151461091a578063f38329d914610943578063fa550eba14610966578063fd73489814610989578063fecc198b1461099c57600080fd5b8063cab7bf561461081b578063cf6bf6f814610846578063d5cb1c5514610859578063dd1d97731461086c578063dd37861d1461087f578063e74e33e014610892578063e81e52ee146108bd578063e94cdf5f146108d057600080fd5b8063aba84bb11161016d578063aba84bb11461075b578063b1b5823b14610785578063be899c891461078e578063bea4ae20146107a1578063c0c53b8b146107b4578063c2002645146107c7578063c3b83f5f146107f0578063c99252881461080357600080fd5b80639168fdd1146106a157806391b4ded9146106ca578063924fbde1146106d357806394bd2313146106e657806397fba4d9146106f9578063a12d5b0714610722578063a13d6b6a14610735578063a2fabfce1461074857600080fd5b806342d851ed116102a55780635c975abb116102485780635c975abb146105b55780635fd5528c146105d257806376d9038f146105fb57806379ba5097146106245780637eda2d7b1461062c578063884806761461063f5780638da5cb5b146106525780638f974cd91461066557806390e3afb21461068e57600080fd5b806342d851ed1461050757806344cab7ee14610530578063485b23bf146105435780634a54bcac146105565780634d1ac474146105695780634fa98ca51461057c57806352aa17a21461058f57806353a47bb7146105a257600080fd5b806316c38b3c1161030d57806316c38b3c1461044c578063188aa45d1461045f57806326f3c6bc14610472578063275fd24714610492578063285e51f9146104a55780633a572121146104b85780633a687e2b146104e15780633ab76e9f146104f457600080fd5b806304de96c51461036b5780630634961a146103945780630652b57a146103a9578063084a2fa4146103bc57806311e9c08d146103cf57806313af4035146103fd5780631590a4a4146104105780631627540c14610439575b600080fd5b60065461037e906001600160a01b031681565b60405161038b9190613ed3565b60405180910390f35b6103a76103a2366004613f1a565b6109c5565b005b6103a76103b7366004613f79565b610c24565b6103a76103ca366004613f96565b610ca9565b6103ef6103dd366004613f79565b600a6020526000908152604090205481565b60405190815260200161038b565b6103a761040b366004613f79565b610cdd565b6103ef61041e366004613f79565b6001600160a01b03166000908152600c602052604090205490565b6103a7610447366004613f79565b610dee565b6103a761045a366004613fd7565b610e41565b6103a761046d366004613ff4565b610eb3565b610485610480366004614020565b610ed3565b60405161038b919061409d565b6103a76104a03660046140f4565b610f03565b6103a76104b336600461415b565b610ffd565b61037e6104c6366004614196565b600b602052600090815260409020546001600160a01b031681565b6103a76104ef3660046141af565b611311565b60145461037e906001600160a01b031681565b61037e610515366004614196565b600f602052600090815260409020546001600160a01b031681565b600e5461037e906001600160a01b031681565b610485610551366004614020565b61143a565b6103a7610564366004614235565b611460565b6103a761057736600461424e565b6116ec565b6103a761058a366004614299565b61189a565b6103a761059d3660046142d9565b61190c565b60015461037e906001600160a01b031681565b6003546105c29060ff1681565b604051901515815260200161038b565b61037e6105e0366004614196565b6015602052600090815260409020546001600160a01b031681565b6103ef610609366004613f79565b6001600160a01b031660009081526018602052604090205490565b6103a7611acd565b6103a761063a366004614313565b611ba5565b61048561064d366004614020565b611e29565b60005461037e906001600160a01b031681565b61037e610673366004613f79565b6009602052600090815260409020546001600160a01b031681565b6103a761069c36600461424e565b611e4f565b6103ef6106af366004613f79565b6001600160a01b03166000908152600d602052604090205490565b6103ef60025481565b6104856106e1366004614352565b611eba565b6103a76106f4366004613f79565b611ee0565b6103ef610707366004613f79565b6001600160a01b031660009081526019602052604090205490565b6103a7610730366004614313565b611f5a565b6103a7610743366004613f79565b611ff9565b610485610756366004614020565b61226a565b61076e610769366004614387565b612290565b60408051921515835260208301919091520161038b565b6103ef60125481565b6103a761079c366004613f96565b6122a8565b6103a76107af3660046143c0565b612340565b6103a76107c2366004614299565b61242b565b6103ef6107d5366004613f79565b6001600160a01b031660009081526016602052604090205490565b6103a76107fe366004613f79565b612574565b60055461037e9061010090046001600160a01b031681565b6103ef610829366004614387565b600760209081526000928352604080842090915290825290205481565b6103a761085436600461441c565b612664565b6103a761086736600461446d565b612a26565b6103a761087a36600461449d565b612b98565b61048561088d366004614020565b612bf3565b6103ef6108a0366004614387565b601060209081526000928352604080842090915290825290205481565b6103a76108cb366004613f79565b612c19565b6103a76108de366004613f79565b612c98565b6104856108f1366004614020565b612d12565b6103a7612d38565b6103ef60115481565b6103a76109153660046144d0565b612d96565b6103ef610928366004613f79565b6001600160a01b031660009081526017602052604090205490565b610956610951366004614352565b612ebf565b60405161038b9493929190614594565b6105c2610974366004613f79565b60086020526000908152604090205460ff1681565b6103a7610997366004614612565b613135565b6103ef6109aa366004613f79565b6001600160a01b031660009081526013602052604090205490565b60145460405163bf40fac160e01b815260206004820152601760248201527629b832b2b226b0b935b2ba39a0a6a6a932b9b7b63b32b960491b60448201526000916001600160a01b03169063bf40fac190606401602060405180830381865afa158015610a36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5a919061464d565b9050336001600160a01b03821614610a855760405163015783e960e51b815260040160405180910390fd5b6001600160a01b038087166000908152600960205260409020541680610abe57604051634f8c20e160e01b815260040160405180910390fd5b6000610acc828689896132e7565b90508315610b5c576001600160a01b0382166000908152601860205260409020610af69089613381565b610b135760405163e31c3b0360e01b815260040160405180910390fd5b6001600160a01b0382166000908152601860205260409020610b3590896133f9565b6001600160a01b0382166000908152601960205260409020610b579089613552565b610bdf565b6001600160a01b0382166000908152601660205260409020610b7e9089613381565b610b9b5760405163e31c3b0360e01b815260040160405180910390fd5b6001600160a01b0382166000908152601660205260409020610bbd90896133f9565b6001600160a01b0382166000908152601760205260409020610bdf9089613552565b7fc89b3148d3afeabb5482b0e35264162df1b695835f82880119e55bcb6a87db46888383604051610c129392919061466a565b60405180910390a15050505050505050565b610c2c6135a4565b6001600160a01b038116610c535760405163e6c4247b60e01b815260040160405180910390fd5b601480546001600160a01b0319166001600160a01b0383161790556040517f954328d28753080b3c499697bde218fd8b53e924669801835383aa346e6940ee90610c9e908390613ed3565b60405180910390a150565b610cb16135a4565b6001600160a01b0392831660009081526010602090815260408083209490951682529290925291902055565b6001600160a01b038116610d345760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064015b60405180910390fd5b600154600160a01b900460ff1615610da05760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610d2b565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b031990911617815560405160008051602061516483398151915291610c9e91849061468e565b610df66135a4565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290610c9e908390613ed3565b610e496135a4565b60035460ff16151581151514610eb0576003805460ff191682151590811790915560ff1615610e7757426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec590602001610c9e565b50565b610ebb6135a4565b610ecf6001600160a01b0383163383613618565b5050565b6001600160a01b0381166000908152601960205260409020606090610ef9908585613675565b90505b9392505050565b60035460ff1615610f265760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254610f39919061471b565b9091555050600454610f496135a4565b6001600160a01b03831660009081526008602052604090205460ff16610f825760405163621a135560e01b815260040160405180910390fd5b60005b84811015610fd4576000868683818110610fa157610fa161472e565b9050602002016020810190610fb69190613f79565b9050610fc381868661378f565b50610fcd81614744565b9050610f85565b506004548114610ff65760405162461bcd60e51b8152600401610d2b9061475d565b5050505050565b60035460ff16156110205760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611033919061471b565b90915550506004543361104c60c0840160a08501613f79565b6001600160a01b03811660009081526008602052604090205460c08501359060ff1661108b5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156110d157604051631e9acf1760e31b815260040160405180910390fd5b6110db8383613934565b6110f85760405163b79adbed60e01b815260040160405180910390fd5b60145460405163bf40fac160e01b81526000916001600160a01b03169063bf40fac19061112790600401614794565b602060405180830381865afa158015611144573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611168919061464d565b90506001600160a01b0381166111915760405163a62e3f5360e01b815260040160405180910390fd5b61119e60808701876147c4565b90506000036111c05760405163f6d764fb60e01b815260040160405180910390fd5b60405163064dbc8760e21b81526000906001600160a01b03831690631936f21c906111ef908a9060040161489d565b6020604051808303816000875af115801561120e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611232919061497b565b60008181526015602090815260409182902080546001600160a01b031916339081179091559293507f4ad010e4ad2be2d7a9851444a5bc66965d9e5c19e9956c1b780a3493efaa3ce19291849160c08c0135918c3591611296918e01908e01614994565b6112a360808e018e6147c4565b604080516001600160a01b03909816885260208801969096529486019390935260608501919091526001600160401b031660808401525060a082015260c0015b60405180910390a150505050506004548114610ecf5760405162461bcd60e51b8152600401610d2b9061475d565b60035460ff16156113345760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611347919061471b565b90915550506004546001600160a01b03821660009081526008602052604090205433908390889060ff1661138e5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156113d457604051631e9acf1760e31b815260040160405180910390fd5b6113de8383613934565b6113fb5760405163b79adbed60e01b815260040160405180910390fd5b61140c8b8b8b8b8b8b8b60006139ab565b50505060045481146114305760405162461bcd60e51b8152600401610d2b9061475d565b5050505050505050565b6001600160a01b0381166000908152600d60205260409020606090610ef9908585613675565b60035460ff16156114835760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611496919061471b565b9091555050600454336114af60e0840160c08501613f79565b6001600160a01b03811660009081526008602052604090205460e08501359060ff166114ee5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481111561153457604051631e9acf1760e31b815260040160405180910390fd5b61153e8383613934565b61155b5760405163b79adbed60e01b815260040160405180910390fd5b60145460405163bf40fac160e01b81526000916001600160a01b03169063bf40fac19061158a90600401614794565b602060405180830381865afa1580156115a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115cb919061464d565b90506001600160a01b0381166115f45760405163a62e3f5360e01b815260040160405180910390fd5b604051636032a02760e01b81526000906001600160a01b03831690636032a02790611623908a906004016149af565b6020604051808303816000875af1158015611642573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611666919061497b565b60008181526015602090815260409182902080546001600160a01b031916339081179091559293507f16b6809ad60f6976d5779745fe0a2d8bd4de64c93c39e8b200cbe1316393dd2b9291849160e08c0135918c35916116ca918e01908e01614994565b6116da60c08e0160a08f01614a6d565b6040516112e396959493929190614a88565b60035460ff161561170f5760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611722919061471b565b909155505060045460005b838110156118725760008585838181106117495761174961472e565b905060200201602081019061175e9190613f79565b6001600160a01b0380821660009081526007602090815260408083209389168352929052205490915015611861576001600160a01b03808216600090815260106020908152604080832093881683529290522054158015906117e457506001600160a01b0380821660009081526010602090815260408083209388168352929052205442115b8061182b57506001600160a01b0380821660009081526010602090815260408083209388168352929052205415801561182b575042601154601254611829919061471b565b105b61184857604051631c8b0d0d60e31b815260040160405180910390fd5b60005461186190829086906001600160a01b031661378f565b5061186b81614744565b905061172d565b5060045481146118945760405162461bcd60e51b8152600401610d2b9061475d565b50505050565b60035460ff16156118bd5760405162461bcd60e51b8152600401610d2b906146a8565b6001600460008282546118d0919061471b565b90915550506004546118e06135a4565b6118eb84848461378f565b60045481146118945760405162461bcd60e51b8152600401610d2b9061475d565b60035460ff161561192f5760405162461bcd60e51b8152600401610d2b906146a8565b3361194060c0830160a08401613f79565b6001600160a01b038116600090815260086020908152604090912054908401359060ff166119815760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156119c757604051631e9acf1760e31b815260040160405180910390fd5b6119d18383613934565b6119ee5760405163b79adbed60e01b815260040160405180910390fd5b600e546040516306fea14960e21b81526000916001600160a01b031690631bfa852490611a1f908890600401614d9b565b6020604051808303816000875af1158015611a3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a62919061497b565b6000818152600f602090815260409182902080546001600160a01b0319163390811790915591519293507f2a107e8d4cba0aa559543eb234520741dac54e7db15d8a96afc8df0dcb8f752592611abe9291890135908590614e21565b60405180910390a15050505050565b6001546001600160a01b03163314611b455760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610d2b565b60005460015460405160008051602061516483398151915292611b76926001600160a01b039182169291169061468e565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b60035460ff1615611bc85760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611bdb919061471b565b9091555050600454600e546001600160a01b03163314611c0e5760405163b0a51f3b60e01b815260040160405180910390fd5b6000858152600f60205260409020546001600160a01b031680611c4457604051630b8efbb360e31b815260040160405180910390fd5b6001600160a01b038316611ccc57600560019054906101000a90046001600160a01b03166001600160a01b031663aeb0f1646040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ca5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc9919061464d565b92505b6001600160a01b03831660009081526008602052604090205460ff16611d055760405163621a135560e01b815260040160405180910390fd5b6001600160a01b03808216600090815260076020908152604080832093871683529290522054841115611d4b57604051631e9acf1760e31b815260040160405180910390fd5b6001600160a01b03808216600090815260076020908152604080832093871683529290529081208054869290611d82908490614e42565b90915550506001600160a01b03858116600090815260096020908152604080832080546001600160a01b0319169486169485179055928252600c905220611dc99086613552565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838585836001604051611dff9493929190614e55565b60405180910390a1506004548114610ff65760405162461bcd60e51b8152600401610d2b9061475d565b6001600160a01b0381166000908152601860205260409020606090610ef9908585613675565b611e576135a4565b60005b8281101561189457611eaa848483818110611e7757611e7761472e565b9050602002016020810190611e8c9190613f79565b6001600160a01b038416600090815260136020526040902090613552565b611eb381614744565b9050611e5a565b6001600160a01b0383166000908152601360205260409020606090610ef9908484613675565b611ee86135a4565b6001600160a01b038116611f0f5760405163e6c4247b60e01b815260040160405180910390fd5b600680546001600160a01b0319166001600160a01b0383161790556040517f8b5670b3bc5c520b7a3580c753ba88065a561ad52a99674196859b044ea2028590610c9e908390613ed3565b60035460ff1615611f7d5760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611f90919061471b565b90915550506004546006546001600160a01b03163314611fc35760405163bae55a2d60e01b815260040160405180910390fd5b6000858152600b60205260409020546001600160a01b031680611c4457604051637692dc6960e01b815260040160405180910390fd5b60055461010090046001600160a01b031633146120295760405163015783e960e51b815260040160405180910390fd5b6001600160a01b03808216600090815260096020526040902054168061206257604051634f8c20e160e01b815260040160405180910390fd5b6001600160a01b0381166000908152600c602052604090206120849083613381565b6120a15760405163e31c3b0360e01b815260040160405180910390fd5b600080836001600160a01b031663242a8a6b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612106919061497b565b90506000846001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015612148573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216c919061464d565b90506000856001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d2919061497b565b90506121e0858385846132e7565b6001600160a01b0386166000908152600c6020526040902090945061220590876133f9565b6001600160a01b0385166000908152600d602052604090206122279087613552565b7f66c0cec01a971dcd7f2bb22f7cc4b244c8f9ee6deb5540b073d0f10b4539e8b586868660405161225a9392919061466a565b60405180910390a1505050505050565b6001600160a01b0381166000908152601660205260409020606090610ef9908585613675565b60008061229d8484613b90565b909590945092505050565b60035460ff16156122cb5760405162461bcd60e51b8152600401610d2b906146a8565b6001600460008282546122de919061471b565b90915550506004546001600160a01b03831660009081526008602052604090205460ff1661231f5760405163621a135560e01b815260040160405180910390fd5b6123346001600160a01b038416333085613c73565b6118eb84848433613c9b565b60035460ff16156123635760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254612376919061471b565b90915550506004546001600160a01b03831660009081526008602052604090205460ff166123b75760405163621a135560e01b815260040160405180910390fd5b6123d833306123c68786614e7f565b6001600160a01b038716929190613c73565b60005b84811015610fd45760008686838181106123f7576123f761472e565b905060200201602081019061240c9190613f79565b905061241a81868633613c9b565b5061242481614744565b90506123db565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03166000811580156124705750825b90506000826001600160401b0316600114801561248c5750303b155b90508115801561249a575080155b156124b85760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156124e257845460ff60401b1916600160401b1785555b6124eb88610cdd565b6124f3612d38565b60058054610100600160a81b0319166101006001600160a01b038a81169190910291909117909155600680546001600160a01b031916918816919091179055831561143057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610c12565b61257c6135a4565b6001600160a01b0381166125c45760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610d2b565b600154600160a81b900460ff16156126145760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610d2b565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b17905560405160008051602061516483398151915291610c9e91849061468e565b60035460ff16156126875760405162461bcd60e51b8152600401610d2b906146a8565b60016004600082825461269a919061471b565b90915550506004805460145460405163bf40fac160e01b815291926000926001600160a01b039092169163bf40fac1916126d49101614794565b602060405180830381865afa1580156126f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612715919061464d565b9050336001600160a01b0382161461274057604051632c58be7f60e11b815260040160405180910390fd5b6001600160a01b0385166128435760145460405163bf40fac160e01b815260206004820152600f60248201526e53706565644d61726b657473414d4d60881b60448201526000916001600160a01b03169063bf40fac190606401602060405180830381865afa1580156127b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127db919061464d565b9050806001600160a01b0316639324cac76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561281b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283f919061464d565b9550505b6001600160a01b03851660009081526008602052604090205460ff1661287c5760405163621a135560e01b815260040160405180910390fd5b6000878152601560205260409020546001600160a01b0316806128b25760405163d287b7b160e01b815260040160405180910390fd5b6001600160a01b038082166000908152600760209081526040808320938a16835292905220548511156128f857604051631e9acf1760e31b815260040160405180910390fd5b6001600160a01b038082166000908152600760209081526040808320938a168352929052908120805487929061292f908490614e42565b90915550506001600160a01b03878116600090815260096020526040902080546001600160a01b031916918316919091179055831561298f576001600160a01b038116600090815260186020526040902061298a9088613552565b6129b1565b6001600160a01b03811660009081526016602052604090206129b19088613552565b604080516001600160a01b0389811682526020820188905283168183015290517fbb19d9066294aca9bb81670382b302c69053c26acb490cfbb522b07ac2bb0b8b9181900360600190a150506004548114612a1e5760405162461bcd60e51b8152600401610d2b9061475d565b505050505050565b612a2e6135a4565b6001600160a01b0383166000908152600860205260409020805460ff19168315801591909117909155612ad45760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390612a8b90849060001990600401614e96565b6020604051808303816000875af1158015612aaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ace9190614eaf565b50612b48565b60405163095ea7b360e01b81526001600160a01b0384169063095ea7b390612b03908490600090600401614e96565b6020604051808303816000875af1158015612b22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b469190614eaf565b505b604080516001600160a01b038581168252841515602083015283168183015290517f6faac0235cc96062eedf703b47b00eaa4680c2cfa3ffae5865f699137264f0159181900360600190a1505050565b612ba06135a4565b60118290558015612bb15780612bb3565b425b60125560408051838152602081018390527fddd57316dedc944a54a2d4ccbec17883e25f23eebb11ea1a4800f36502a469f0910160405180910390a15050565b6001600160a01b0381166000908152600c60205260409020606090610ef9908585613675565b612c216135a4565b6001600160a01b038116612c485760405163e6c4247b60e01b815260040160405180910390fd5b60058054610100600160a81b0319166101006001600160a01b038416021790556040517f9985022676a73860c32a3b91ea7a7dfe2d5e87c148f50eb519d8b0f33ab7f8b990610c9e908390613ed3565b612ca06135a4565b6001600160a01b038116612cc75760405163e6c4247b60e01b815260040160405180910390fd5b600e80546001600160a01b0319166001600160a01b0383161790556040517f63bb8d8b29c197c8b58de8e513ab743bce7bf84c87adc05f2cc18e609d5d0e8690610c9e908390613ed3565b6001600160a01b0381166000908152601760205260409020606090610ef9908585613675565b60055460ff1615612d815760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b6044820152606401610d2b565b6005805460ff19166001908117909155600455565b60035460ff1615612db95760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254612dcc919061471b565b90915550506004546001600160a01b03831660009081526008602052604090205433908490899060ff16612e135760405163621a135560e01b815260040160405180910390fd5b6001600160a01b03808416600090815260076020908152604080832093861683529290522054811115612e5957604051631e9acf1760e31b815260040160405180910390fd5b612e638383613934565b612e805760405163b79adbed60e01b815260040160405180910390fd5b612e908c8c8c8c8c8c8c8c6139ab565b5050506004548114612eb45760405162461bcd60e51b8152600401610d2b9061475d565b505050505050505050565b6001600160a01b038316600090815260136020526040902054606090819081908190851115612f04576001600160a01b03871660009081526013602052604090205494505b846001600160401b03811115612f1c57612f1c614ecc565b604051908082528060200260200182016040528015612f45578160200160208202803683370190505b509350846001600160401b03811115612f6057612f60614ecc565b604051908082528060200260200182016040528015612f89578160200160208202803683370190505b509150846001600160401b03811115612fa457612fa4614ecc565b604051908082528060200260200182016040528015612fcd578160200160208202803683370190505b509250846001600160401b03811115612fe857612fe8614ecc565b604051908082528060200260200182016040528015613011578160200160208202803683370190505b50905060005b8581101561312b576001600160a01b0388166000908152601360205260408120613041838a61471b565b815481106130515761305161472e565b6000918252602090912001546001600160a01b03169050613072818a613b90565b8584815181106130845761308461472e565b6020026020010185858151811061309d5761309d61472e565b602002602001018281525082151515158152505050808683815181106130c5576130c561472e565b6001600160a01b039283166020918202929092018101919091528282166000908152600782526040808220938d168252929091522054855186908490811061310f5761310f61472e565b60209081029190910101525061312481614744565b9050613017565b5093509350935093565b60035460ff16156131585760405162461bcd60e51b8152600401610d2b906146a8565b3361316b61014083016101208401613f79565b6001600160a01b03811660009081526008602052604090205460a08401359060ff166131aa5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156131f057604051631e9acf1760e31b815260040160405180910390fd5b6131fa8383613934565b6132175760405163b79adbed60e01b815260040160405180910390fd5b60065460405163584ceab960e11b81526000916001600160a01b03169063b099d57290613248908890600401614f50565b6020604051808303816000875af1158015613267573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061328b919061497b565b6000818152600b60205260409081902080546001600160a01b0319163390811790915590519192507ff226e2c0a5c137f1fc5331d8dabe76ea9810e121bc9b18737125bcaa884daa8091611abe919060a0890135908590614e21565b60008215613379578183111561333c57600054613311906001600160a01b03868116911684613618565b61331b8284614e42565b90508015613337576133376001600160a01b0385168683613618565b613379565b6001600160a01b0380861660009081526007602090815260408083209388168352929052908120805485929061337390849061471b565b90915550505b949350505050565b81546000908103613394575060006133f3565b6001600160a01b0382166000908152600184016020526040902054801515806133ef5750826001600160a01b0316846000016000815481106133d8576133d861472e565b6000918252602090912001546001600160a01b0316145b9150505b92915050565b6134038282613381565b6134455760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b6044820152606401610d2b565b6001600160a01b038116600090815260018084016020526040822054845490929161346f91614e42565b90508082146134fb57600084600001828154811061348f5761348f61472e565b60009182526020909120015485546001600160a01b03909116915081908690859081106134be576134be61472e565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b835484908061350c5761350c615071565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b61355c8282613381565b610ecf5781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555050565b6000546001600160a01b031633146136165760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610d2b565b565b61367083846001600160a01b031663a9059cbb858560405160240161363e929190614e96565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613d7b565b505050565b60606000613683838561471b565b8554909150811115613693575083545b8381116136b0575050604080516000815260208101909152610efc565b60006136bc8583614e42565b90506000816001600160401b038111156136d8576136d8614ecc565b604051908082528060200260200182016040528015613701578160200160208202803683370190505b50905060005b82811015613784578761371a888361471b565b8154811061372a5761372a61472e565b9060005260206000200160009054906101000a90046001600160a01b031682828151811061375a5761375a61472e565b6001600160a01b03909216602092830291909101909101528061377c81614744565b915050613707565b509695505050505050565b6001600160a01b03821660009081526008602052604090205460ff166137c85760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616808452939091528082205490516370a0823160e01b81529092906370a0823190613815903090600401613ed3565b602060405180830381865afa158015613832573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613856919061497b565b90506000821180156138685750818110155b15613881576138816001600160a01b0385168484613618565b6001600160a01b038086166000908152600760209081526040808320938816835292815282822082905560139052206138ba9086613381565b156138e1576001600160a01b03841660009081526013602052604090206138e190866133f9565b604080516001600160a01b0380881682528087166020830152851691810191909152606081018390527f5aecd162bb2e2f8d70294fe483d6a8596cf8555a650f08a962220feaba5c21fe90608001611abe565b6001600160a01b038083166000908152601060209081526040808320938516835292905290812054421080610efc57506001600160a01b03808416600090815260106020908152604080832093861683529290522054158015610efc5750426011546012546139a3919061471b565b119392505050565b3360009081526007602090815260408083206001600160a01b0386168452909152812080548892906139de908490614e42565b909155506000905060ff821615613a7e576005546040516349d8615760e11b81526101009091046001600160a01b0316906393b0c2ae90613a34908c908c908c908c908c908c908c906000908d90600401615087565b6020604051808303816000875af1158015613a53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a77919061464d565b9050613b06565b6005546040516214e17b60e41b81526101009091046001600160a01b03169063014e17b090613ac0908c908c908c908c908c908c908c906000906004016150e3565b6020604051808303816000875af1158015613adf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b03919061464d565b90505b6001600160a01b038116600090815260096020908152604080832080546001600160a01b031916339081179091558352600c9091529020613b479082613552565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838188336000604051613b7d9493929190614e55565b60405180910390a1505050505050505050565b6001600160a01b038116600090815260086020526040812054819060ff168015613bde57506001600160a01b0380851660009081526007602090815260408083209387168352929052205415155b15613c6c576001600160a01b038085166000908152601060209081526040808320938716835292905290812054613c2457601154601254613c1f919061471b565b613c4b565b6001600160a01b038086166000908152601060209081526040808320938816835292905220545b9050428111925082613c5e576000613c68565b613c684282614e42565b9150505b9250929050565b61189484856001600160a01b03166323b872dd86868660405160240161363e9392919061466a565b6001600160a01b0383166000908152601360205260409020613cbd9085613552565b6001600160a01b03808516600090815260076020908152604080832093871683529290529081208054849290613cf490849061471b565b9091555050601154613d06904261471b565b6001600160a01b03858116600081815260106020908152604080832089861680855290835292819020959095558451928352820152918201849052821660608201527ff462a3a00cd477c2aa6252dfbff27a7cf55ad6f75c526f4650b5bb92ef2912689060800160405180910390a150505050565b6000613d906001600160a01b03841683613dd5565b90508051600014158015613db5575080806020019051810190613db39190614eaf565b155b156136705782604051635274afe760e01b8152600401610d2b9190613ed3565b6060610efc8383600084600080856001600160a01b03168486604051613dfb9190615134565b60006040518083038185875af1925050503d8060008114613e38576040519150601f19603f3d011682016040523d82523d6000602084013e613e3d565b606091505b5091509150613e4d868383613e57565b9695505050505050565b606082613e6c57613e6782613eaa565b610efc565b8151158015613e8357506001600160a01b0384163b155b15613ea35783604051639996b31560e01b8152600401610d2b9190613ed3565b5080610efc565b805115613eba5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0391909116815260200190565b6001600160a01b0381168114610eb057600080fd5b8035613f0781613ee7565b919050565b8015158114610eb057600080fd5b600080600080600060a08688031215613f3257600080fd5b8535613f3d81613ee7565b945060208601359350604086013592506060860135613f5b81613ee7565b91506080860135613f6b81613f0c565b809150509295509295909350565b600060208284031215613f8b57600080fd5b8135610efc81613ee7565b600080600060608486031215613fab57600080fd5b8335613fb681613ee7565b92506020840135613fc681613ee7565b929592945050506040919091013590565b600060208284031215613fe957600080fd5b8135610efc81613f0c565b6000806040838503121561400757600080fd5b823561401281613ee7565b946020939093013593505050565b60008060006060848603121561403557600080fd5b8335925060208401359150604084013561404e81613ee7565b809150509250925092565b600081518084526020808501945080840160005b838110156140925781516001600160a01b03168752958201959082019060010161406d565b509495945050505050565b602081526000610efc6020830184614059565b60008083601f8401126140c257600080fd5b5081356001600160401b038111156140d957600080fd5b6020830191508360208260051b8501011115613c6c57600080fd5b6000806000806060858703121561410a57600080fd5b84356001600160401b0381111561412057600080fd5b61412c878288016140b0565b909550935050602085013561414081613ee7565b9150604085013561415081613ee7565b939692955090935050565b60006020828403121561416d57600080fd5b81356001600160401b0381111561418357600080fd5b82016101008185031215610efc57600080fd5b6000602082840312156141a857600080fd5b5035919050565b600080600080600080600060c0888a0312156141ca57600080fd5b87356001600160401b038111156141e057600080fd5b6141ec8a828b016140b0565b909850965050602088013594506040880135935060608801359250608088013561421581613ee7565b915060a088013561422581613ee7565b8091505092959891949750929550565b6000610140828403121561424857600080fd5b50919050565b60008060006040848603121561426357600080fd5b83356001600160401b0381111561427957600080fd5b614285868287016140b0565b909450925050602084013561404e81613ee7565b6000806000606084860312156142ae57600080fd5b83356142b981613ee7565b925060208401356142c981613ee7565b9150604084013561404e81613ee7565b6000602082840312156142eb57600080fd5b81356001600160401b0381111561430157600080fd5b820160c08185031215610efc57600080fd5b6000806000806080858703121561432957600080fd5b84359350602085013561433b81613ee7565b925060408501359150606085013561415081613ee7565b60008060006060848603121561436757600080fd5b833561437281613ee7565b95602085013595506040909401359392505050565b6000806040838503121561439a57600080fd5b82356143a581613ee7565b915060208301356143b581613ee7565b809150509250929050565b600080600080606085870312156143d657600080fd5b84356001600160401b038111156143ec57600080fd5b6143f8878288016140b0565b909550935050602085013561440c81613ee7565b9396929550929360400135925050565b600080600080600060a0868803121561443457600080fd5b85359450602086013561444681613ee7565b9350604086013561445681613ee7565b9250606086013591506080860135613f6b81613f0c565b60008060006060848603121561448257600080fd5b833561448d81613ee7565b925060208401356142c981613f0c565b600080604083850312156144b057600080fd5b50508035926020909101359150565b803560ff81168114613f0757600080fd5b60008060008060008060008060e0898b0312156144ec57600080fd5b88356001600160401b0381111561450257600080fd5b61450e8b828c016140b0565b909950975050602089013595506040890135945060608901359350608089013561453781613ee7565b925060a089013561454781613ee7565b915061455560c08a016144bf565b90509295985092959890939650565b600081518084526020808501945080840160005b8381101561409257815187529582019590820190600101614578565b6080815260006145a76080830187614059565b6020838203818501526145ba8288614564565b8481036040860152865180825282880193509082019060005b818110156145f15784511515835293830193918301916001016145d3565b505084810360608601526146058187614564565b9998505050505050505050565b60006020828403121561462457600080fd5b81356001600160401b0381111561463a57600080fd5b82016101608185031215610efc57600080fd5b60006020828403121561465f57600080fd5b8151610efc81613ee7565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0392831681529116602082015260400190565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b634e487b7160e01b600052601160045260246000fd5b808201808211156133f3576133f3614705565b634e487b7160e01b600052603260045260246000fd5b60006001820161475657614756614705565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526016908201527529b832b2b226b0b935b2ba39a0a6a6a1b932b0ba37b960511b604082015260600190565b6000808335601e198436030181126147db57600080fd5b8301803591506001600160401b038211156147f557600080fd5b6020019150600581901b3603821315613c6c57600080fd5b80356001600160401b0381168114613f0757600080fd5b6000808335601e1984360301811261483b57600080fd5b83016020810192503590506001600160401b0381111561485a57600080fd5b8060051b3603821315613c6c57600080fd5b6002811061488a57634e487b7160e01b600052602160045260246000fd5b9052565b803560028110613f0757600080fd5b6000602080835261012083018435828501526001600160401b036148c283870161480d565b16604085015260408501356060850152606085013560808501526148e96080860186614824565b61010060a087018190529281905290916000919061014087015b8284101561492f5761491d816149188761488e565b61486c565b93850193600193909301928501614903565b61493b60a08a01613efc565b6001600160a01b03811660c08a0152955060c089013560e089015261496260e08a01613efc565b6001600160a01b03169190970152509395945050505050565b60006020828403121561498d57600080fd5b5051919050565b6000602082840312156149a657600080fd5b610efc8261480d565b8135815261014081016149c46020840161480d565b6001600160401b031660208301526149de6040840161480d565b6001600160401b0381166040840152506060830135606083015260808301356080830152614a0e60a0840161488e565b614a1b60a084018261486c565b50614a2860c08401613efc565b6001600160a01b031660c083015260e08381013590830152610100614a4e818501613efc565b6001600160a01b03811684830152505061012092830135919092015290565b600060208284031215614a7f57600080fd5b610efc8261488e565b6001600160a01b03871681526020810186905260408101859052606081018490526001600160401b038316608082015260c08101614ac960a083018461486c565b979650505050505050565b803561ffff81168114613f0757600080fd5b8035600281900b8114613f0757600080fd5b803562ffffff81168114613f0757600080fd5b81835260006001600160fb1b03831115614b2457600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b858110156140925761ffff614b6183614ad4565b16875260ff614b718484016144bf565b16838801526040614b83818401614ae6565b60020b908801526060968701969190910190600101614b4d565b81835260006020808501808196508560051b810191508460005b87811015614c245782840389528135601e19883603018112614bd857600080fd5b870185810190356001600160401b03811115614bf357600080fd5b606081023603821315614c0557600080fd5b614c10868284614b3d565b9a87019a9550505090840190600101614bb7565b5091979650505050505050565b6000823561015e19833603018112614c4857600080fd5b90910192915050565b81835260006020808501808196508560051b810191508460005b87811015614c24578284038952614c828288614c31565b61016081358652614c94878301614ad4565b61ffff16878701526040614ca9838201614ad4565b61ffff1690870152606082810135908701526080614cc88184016144bf565b60ff169087015260a0614cdc838201614ae6565b614cea8289018260020b9052565b505060c0614cf9818401614af8565b62ffffff169087015260e0614d1083820184614824565b83838a0152614d22848a018284614b0b565b9350505050610100614d3681840184614824565b888403838a0152614d48848284614b0b565b9350505050610120614d5b8184016144bf565b60ff1690870152610140614d7183820184614824565b935087830382890152614d85838583614b9d565b9c89019c97505050928601925050600101614c6b565b602081526000614dab8384614824565b60c06020850152614dc060e085018284614c51565b9150506020840135604084015260408401356060840152606084013560808401526080840135614def81613ee7565b6001600160a01b0390811660a08581019190915285013590614e1082613ee7565b1660c0939093019290925250919050565b6001600160a01b039390931683526020830191909152604082015260600190565b818103818111156133f3576133f3614705565b6001600160a01b039485168152602081019390935292166040820152901515606082015260800190565b80820281158282048414176133f3576133f3614705565b6001600160a01b03929092168252602082015260400190565b600060208284031215614ec157600080fd5b8151610efc81613f0c565b634e487b7160e01b600052604160045260246000fd5b6000808335601e19843603018112614ef957600080fd5b83016020810192503590506001600160401b03811115614f1857600080fd5b803603821315613c6c57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000614f608384614ee2565b610160806020860152614f7861018086018385614f27565b9250614f8660208701614ad4565b61ffff811660408701529150614f9e60408701614ad4565b61ffff811660608701529150614fb660608701614ae6565b9150614fc7608086018360020b9052565b614fd3608087016144bf565b60ff811660a0870152915060a086013560c086015260c086013560e0860152610100915060e08601358286015261500b828701613efc565b9150610120615024818701846001600160a01b03169052565b61502f818801613efc565b925050610140615049818701846001600160a01b03169052565b615054818801614af8565b9250506150678186018362ffffff169052565b5090949350505050565b634e487b7160e01b600052603160045260246000fd5b600061010080835261509c8184018c8e614c51565b602084019a909a525050604081019690965260608601949094526001600160a01b039283166080860152911660a0840152151560c083015260ff1660e09091015292915050565b60e0815260006150f760e083018a8c614c51565b602083019890985250604081019590955260608501939093526001600160a01b0391821660808501521660a0830152151560c09091015292915050565b6000825160005b81811015615155576020818601810151858301520161513b565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca26469706673582212203e2b794f930d8c1d4f38ca80981a7f46737630b7b494a6e4a25994de068b6eea64736f6c63430008140033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103665760003560e01c80639168fdd1116101ca578063cab7bf5611610105578063ea7e3518116100a8578063ea7e3518146108e3578063ebc79772146108f6578063edc5de8e146108fe578063efac480d14610907578063f0b9c9151461091a578063f38329d914610943578063fa550eba14610966578063fd73489814610989578063fecc198b1461099c57600080fd5b8063cab7bf561461081b578063cf6bf6f814610846578063d5cb1c5514610859578063dd1d97731461086c578063dd37861d1461087f578063e74e33e014610892578063e81e52ee146108bd578063e94cdf5f146108d057600080fd5b8063aba84bb11161016d578063aba84bb11461075b578063b1b5823b14610785578063be899c891461078e578063bea4ae20146107a1578063c0c53b8b146107b4578063c2002645146107c7578063c3b83f5f146107f0578063c99252881461080357600080fd5b80639168fdd1146106a157806391b4ded9146106ca578063924fbde1146106d357806394bd2313146106e657806397fba4d9146106f9578063a12d5b0714610722578063a13d6b6a14610735578063a2fabfce1461074857600080fd5b806342d851ed116102a55780635c975abb116102485780635c975abb146105b55780635fd5528c146105d257806376d9038f146105fb57806379ba5097146106245780637eda2d7b1461062c578063884806761461063f5780638da5cb5b146106525780638f974cd91461066557806390e3afb21461068e57600080fd5b806342d851ed1461050757806344cab7ee14610530578063485b23bf146105435780634a54bcac146105565780634d1ac474146105695780634fa98ca51461057c57806352aa17a21461058f57806353a47bb7146105a257600080fd5b806316c38b3c1161030d57806316c38b3c1461044c578063188aa45d1461045f57806326f3c6bc14610472578063275fd24714610492578063285e51f9146104a55780633a572121146104b85780633a687e2b146104e15780633ab76e9f146104f457600080fd5b806304de96c51461036b5780630634961a146103945780630652b57a146103a9578063084a2fa4146103bc57806311e9c08d146103cf57806313af4035146103fd5780631590a4a4146104105780631627540c14610439575b600080fd5b60065461037e906001600160a01b031681565b60405161038b9190613ed3565b60405180910390f35b6103a76103a2366004613f1a565b6109c5565b005b6103a76103b7366004613f79565b610c24565b6103a76103ca366004613f96565b610ca9565b6103ef6103dd366004613f79565b600a6020526000908152604090205481565b60405190815260200161038b565b6103a761040b366004613f79565b610cdd565b6103ef61041e366004613f79565b6001600160a01b03166000908152600c602052604090205490565b6103a7610447366004613f79565b610dee565b6103a761045a366004613fd7565b610e41565b6103a761046d366004613ff4565b610eb3565b610485610480366004614020565b610ed3565b60405161038b919061409d565b6103a76104a03660046140f4565b610f03565b6103a76104b336600461415b565b610ffd565b61037e6104c6366004614196565b600b602052600090815260409020546001600160a01b031681565b6103a76104ef3660046141af565b611311565b60145461037e906001600160a01b031681565b61037e610515366004614196565b600f602052600090815260409020546001600160a01b031681565b600e5461037e906001600160a01b031681565b610485610551366004614020565b61143a565b6103a7610564366004614235565b611460565b6103a761057736600461424e565b6116ec565b6103a761058a366004614299565b61189a565b6103a761059d3660046142d9565b61190c565b60015461037e906001600160a01b031681565b6003546105c29060ff1681565b604051901515815260200161038b565b61037e6105e0366004614196565b6015602052600090815260409020546001600160a01b031681565b6103ef610609366004613f79565b6001600160a01b031660009081526018602052604090205490565b6103a7611acd565b6103a761063a366004614313565b611ba5565b61048561064d366004614020565b611e29565b60005461037e906001600160a01b031681565b61037e610673366004613f79565b6009602052600090815260409020546001600160a01b031681565b6103a761069c36600461424e565b611e4f565b6103ef6106af366004613f79565b6001600160a01b03166000908152600d602052604090205490565b6103ef60025481565b6104856106e1366004614352565b611eba565b6103a76106f4366004613f79565b611ee0565b6103ef610707366004613f79565b6001600160a01b031660009081526019602052604090205490565b6103a7610730366004614313565b611f5a565b6103a7610743366004613f79565b611ff9565b610485610756366004614020565b61226a565b61076e610769366004614387565b612290565b60408051921515835260208301919091520161038b565b6103ef60125481565b6103a761079c366004613f96565b6122a8565b6103a76107af3660046143c0565b612340565b6103a76107c2366004614299565b61242b565b6103ef6107d5366004613f79565b6001600160a01b031660009081526016602052604090205490565b6103a76107fe366004613f79565b612574565b60055461037e9061010090046001600160a01b031681565b6103ef610829366004614387565b600760209081526000928352604080842090915290825290205481565b6103a761085436600461441c565b612664565b6103a761086736600461446d565b612a26565b6103a761087a36600461449d565b612b98565b61048561088d366004614020565b612bf3565b6103ef6108a0366004614387565b601060209081526000928352604080842090915290825290205481565b6103a76108cb366004613f79565b612c19565b6103a76108de366004613f79565b612c98565b6104856108f1366004614020565b612d12565b6103a7612d38565b6103ef60115481565b6103a76109153660046144d0565b612d96565b6103ef610928366004613f79565b6001600160a01b031660009081526017602052604090205490565b610956610951366004614352565b612ebf565b60405161038b9493929190614594565b6105c2610974366004613f79565b60086020526000908152604090205460ff1681565b6103a7610997366004614612565b613135565b6103ef6109aa366004613f79565b6001600160a01b031660009081526013602052604090205490565b60145460405163bf40fac160e01b815260206004820152601760248201527629b832b2b226b0b935b2ba39a0a6a6a932b9b7b63b32b960491b60448201526000916001600160a01b03169063bf40fac190606401602060405180830381865afa158015610a36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a5a919061464d565b9050336001600160a01b03821614610a855760405163015783e960e51b815260040160405180910390fd5b6001600160a01b038087166000908152600960205260409020541680610abe57604051634f8c20e160e01b815260040160405180910390fd5b6000610acc828689896132e7565b90508315610b5c576001600160a01b0382166000908152601860205260409020610af69089613381565b610b135760405163e31c3b0360e01b815260040160405180910390fd5b6001600160a01b0382166000908152601860205260409020610b3590896133f9565b6001600160a01b0382166000908152601960205260409020610b579089613552565b610bdf565b6001600160a01b0382166000908152601660205260409020610b7e9089613381565b610b9b5760405163e31c3b0360e01b815260040160405180910390fd5b6001600160a01b0382166000908152601660205260409020610bbd90896133f9565b6001600160a01b0382166000908152601760205260409020610bdf9089613552565b7fc89b3148d3afeabb5482b0e35264162df1b695835f82880119e55bcb6a87db46888383604051610c129392919061466a565b60405180910390a15050505050505050565b610c2c6135a4565b6001600160a01b038116610c535760405163e6c4247b60e01b815260040160405180910390fd5b601480546001600160a01b0319166001600160a01b0383161790556040517f954328d28753080b3c499697bde218fd8b53e924669801835383aa346e6940ee90610c9e908390613ed3565b60405180910390a150565b610cb16135a4565b6001600160a01b0392831660009081526010602090815260408083209490951682529290925291902055565b6001600160a01b038116610d345760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064015b60405180910390fd5b600154600160a01b900460ff1615610da05760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610d2b565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b031990911617815560405160008051602061516483398151915291610c9e91849061468e565b610df66135a4565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290610c9e908390613ed3565b610e496135a4565b60035460ff16151581151514610eb0576003805460ff191682151590811790915560ff1615610e7757426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec590602001610c9e565b50565b610ebb6135a4565b610ecf6001600160a01b0383163383613618565b5050565b6001600160a01b0381166000908152601960205260409020606090610ef9908585613675565b90505b9392505050565b60035460ff1615610f265760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254610f39919061471b565b9091555050600454610f496135a4565b6001600160a01b03831660009081526008602052604090205460ff16610f825760405163621a135560e01b815260040160405180910390fd5b60005b84811015610fd4576000868683818110610fa157610fa161472e565b9050602002016020810190610fb69190613f79565b9050610fc381868661378f565b50610fcd81614744565b9050610f85565b506004548114610ff65760405162461bcd60e51b8152600401610d2b9061475d565b5050505050565b60035460ff16156110205760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611033919061471b565b90915550506004543361104c60c0840160a08501613f79565b6001600160a01b03811660009081526008602052604090205460c08501359060ff1661108b5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156110d157604051631e9acf1760e31b815260040160405180910390fd5b6110db8383613934565b6110f85760405163b79adbed60e01b815260040160405180910390fd5b60145460405163bf40fac160e01b81526000916001600160a01b03169063bf40fac19061112790600401614794565b602060405180830381865afa158015611144573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611168919061464d565b90506001600160a01b0381166111915760405163a62e3f5360e01b815260040160405180910390fd5b61119e60808701876147c4565b90506000036111c05760405163f6d764fb60e01b815260040160405180910390fd5b60405163064dbc8760e21b81526000906001600160a01b03831690631936f21c906111ef908a9060040161489d565b6020604051808303816000875af115801561120e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611232919061497b565b60008181526015602090815260409182902080546001600160a01b031916339081179091559293507f4ad010e4ad2be2d7a9851444a5bc66965d9e5c19e9956c1b780a3493efaa3ce19291849160c08c0135918c3591611296918e01908e01614994565b6112a360808e018e6147c4565b604080516001600160a01b03909816885260208801969096529486019390935260608501919091526001600160401b031660808401525060a082015260c0015b60405180910390a150505050506004548114610ecf5760405162461bcd60e51b8152600401610d2b9061475d565b60035460ff16156113345760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611347919061471b565b90915550506004546001600160a01b03821660009081526008602052604090205433908390889060ff1661138e5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156113d457604051631e9acf1760e31b815260040160405180910390fd5b6113de8383613934565b6113fb5760405163b79adbed60e01b815260040160405180910390fd5b61140c8b8b8b8b8b8b8b60006139ab565b50505060045481146114305760405162461bcd60e51b8152600401610d2b9061475d565b5050505050505050565b6001600160a01b0381166000908152600d60205260409020606090610ef9908585613675565b60035460ff16156114835760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611496919061471b565b9091555050600454336114af60e0840160c08501613f79565b6001600160a01b03811660009081526008602052604090205460e08501359060ff166114ee5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481111561153457604051631e9acf1760e31b815260040160405180910390fd5b61153e8383613934565b61155b5760405163b79adbed60e01b815260040160405180910390fd5b60145460405163bf40fac160e01b81526000916001600160a01b03169063bf40fac19061158a90600401614794565b602060405180830381865afa1580156115a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115cb919061464d565b90506001600160a01b0381166115f45760405163a62e3f5360e01b815260040160405180910390fd5b604051636032a02760e01b81526000906001600160a01b03831690636032a02790611623908a906004016149af565b6020604051808303816000875af1158015611642573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611666919061497b565b60008181526015602090815260409182902080546001600160a01b031916339081179091559293507f16b6809ad60f6976d5779745fe0a2d8bd4de64c93c39e8b200cbe1316393dd2b9291849160e08c0135918c35916116ca918e01908e01614994565b6116da60c08e0160a08f01614a6d565b6040516112e396959493929190614a88565b60035460ff161561170f5760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611722919061471b565b909155505060045460005b838110156118725760008585838181106117495761174961472e565b905060200201602081019061175e9190613f79565b6001600160a01b0380821660009081526007602090815260408083209389168352929052205490915015611861576001600160a01b03808216600090815260106020908152604080832093881683529290522054158015906117e457506001600160a01b0380821660009081526010602090815260408083209388168352929052205442115b8061182b57506001600160a01b0380821660009081526010602090815260408083209388168352929052205415801561182b575042601154601254611829919061471b565b105b61184857604051631c8b0d0d60e31b815260040160405180910390fd5b60005461186190829086906001600160a01b031661378f565b5061186b81614744565b905061172d565b5060045481146118945760405162461bcd60e51b8152600401610d2b9061475d565b50505050565b60035460ff16156118bd5760405162461bcd60e51b8152600401610d2b906146a8565b6001600460008282546118d0919061471b565b90915550506004546118e06135a4565b6118eb84848461378f565b60045481146118945760405162461bcd60e51b8152600401610d2b9061475d565b60035460ff161561192f5760405162461bcd60e51b8152600401610d2b906146a8565b3361194060c0830160a08401613f79565b6001600160a01b038116600090815260086020908152604090912054908401359060ff166119815760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156119c757604051631e9acf1760e31b815260040160405180910390fd5b6119d18383613934565b6119ee5760405163b79adbed60e01b815260040160405180910390fd5b600e546040516306fea14960e21b81526000916001600160a01b031690631bfa852490611a1f908890600401614d9b565b6020604051808303816000875af1158015611a3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a62919061497b565b6000818152600f602090815260409182902080546001600160a01b0319163390811790915591519293507f2a107e8d4cba0aa559543eb234520741dac54e7db15d8a96afc8df0dcb8f752592611abe9291890135908590614e21565b60405180910390a15050505050565b6001546001600160a01b03163314611b455760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610d2b565b60005460015460405160008051602061516483398151915292611b76926001600160a01b039182169291169061468e565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b60035460ff1615611bc85760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611bdb919061471b565b9091555050600454600e546001600160a01b03163314611c0e5760405163b0a51f3b60e01b815260040160405180910390fd5b6000858152600f60205260409020546001600160a01b031680611c4457604051630b8efbb360e31b815260040160405180910390fd5b6001600160a01b038316611ccc57600560019054906101000a90046001600160a01b03166001600160a01b031663aeb0f1646040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ca5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc9919061464d565b92505b6001600160a01b03831660009081526008602052604090205460ff16611d055760405163621a135560e01b815260040160405180910390fd5b6001600160a01b03808216600090815260076020908152604080832093871683529290522054841115611d4b57604051631e9acf1760e31b815260040160405180910390fd5b6001600160a01b03808216600090815260076020908152604080832093871683529290529081208054869290611d82908490614e42565b90915550506001600160a01b03858116600090815260096020908152604080832080546001600160a01b0319169486169485179055928252600c905220611dc99086613552565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838585836001604051611dff9493929190614e55565b60405180910390a1506004548114610ff65760405162461bcd60e51b8152600401610d2b9061475d565b6001600160a01b0381166000908152601860205260409020606090610ef9908585613675565b611e576135a4565b60005b8281101561189457611eaa848483818110611e7757611e7761472e565b9050602002016020810190611e8c9190613f79565b6001600160a01b038416600090815260136020526040902090613552565b611eb381614744565b9050611e5a565b6001600160a01b0383166000908152601360205260409020606090610ef9908484613675565b611ee86135a4565b6001600160a01b038116611f0f5760405163e6c4247b60e01b815260040160405180910390fd5b600680546001600160a01b0319166001600160a01b0383161790556040517f8b5670b3bc5c520b7a3580c753ba88065a561ad52a99674196859b044ea2028590610c9e908390613ed3565b60035460ff1615611f7d5760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254611f90919061471b565b90915550506004546006546001600160a01b03163314611fc35760405163bae55a2d60e01b815260040160405180910390fd5b6000858152600b60205260409020546001600160a01b031680611c4457604051637692dc6960e01b815260040160405180910390fd5b60055461010090046001600160a01b031633146120295760405163015783e960e51b815260040160405180910390fd5b6001600160a01b03808216600090815260096020526040902054168061206257604051634f8c20e160e01b815260040160405180910390fd5b6001600160a01b0381166000908152600c602052604090206120849083613381565b6120a15760405163e31c3b0360e01b815260040160405180910390fd5b600080836001600160a01b031663242a8a6b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120e2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612106919061497b565b90506000846001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015612148573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216c919061464d565b90506000856001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d2919061497b565b90506121e0858385846132e7565b6001600160a01b0386166000908152600c6020526040902090945061220590876133f9565b6001600160a01b0385166000908152600d602052604090206122279087613552565b7f66c0cec01a971dcd7f2bb22f7cc4b244c8f9ee6deb5540b073d0f10b4539e8b586868660405161225a9392919061466a565b60405180910390a1505050505050565b6001600160a01b0381166000908152601660205260409020606090610ef9908585613675565b60008061229d8484613b90565b909590945092505050565b60035460ff16156122cb5760405162461bcd60e51b8152600401610d2b906146a8565b6001600460008282546122de919061471b565b90915550506004546001600160a01b03831660009081526008602052604090205460ff1661231f5760405163621a135560e01b815260040160405180910390fd5b6123346001600160a01b038416333085613c73565b6118eb84848433613c9b565b60035460ff16156123635760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254612376919061471b565b90915550506004546001600160a01b03831660009081526008602052604090205460ff166123b75760405163621a135560e01b815260040160405180910390fd5b6123d833306123c68786614e7f565b6001600160a01b038716929190613c73565b60005b84811015610fd45760008686838181106123f7576123f761472e565b905060200201602081019061240c9190613f79565b905061241a81868633613c9b565b5061242481614744565b90506123db565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03166000811580156124705750825b90506000826001600160401b0316600114801561248c5750303b155b90508115801561249a575080155b156124b85760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156124e257845460ff60401b1916600160401b1785555b6124eb88610cdd565b6124f3612d38565b60058054610100600160a81b0319166101006001600160a01b038a81169190910291909117909155600680546001600160a01b031916918816919091179055831561143057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610c12565b61257c6135a4565b6001600160a01b0381166125c45760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610d2b565b600154600160a81b900460ff16156126145760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610d2b565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b17905560405160008051602061516483398151915291610c9e91849061468e565b60035460ff16156126875760405162461bcd60e51b8152600401610d2b906146a8565b60016004600082825461269a919061471b565b90915550506004805460145460405163bf40fac160e01b815291926000926001600160a01b039092169163bf40fac1916126d49101614794565b602060405180830381865afa1580156126f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612715919061464d565b9050336001600160a01b0382161461274057604051632c58be7f60e11b815260040160405180910390fd5b6001600160a01b0385166128435760145460405163bf40fac160e01b815260206004820152600f60248201526e53706565644d61726b657473414d4d60881b60448201526000916001600160a01b03169063bf40fac190606401602060405180830381865afa1580156127b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127db919061464d565b9050806001600160a01b0316639324cac76040518163ffffffff1660e01b8152600401602060405180830381865afa15801561281b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061283f919061464d565b9550505b6001600160a01b03851660009081526008602052604090205460ff1661287c5760405163621a135560e01b815260040160405180910390fd5b6000878152601560205260409020546001600160a01b0316806128b25760405163d287b7b160e01b815260040160405180910390fd5b6001600160a01b038082166000908152600760209081526040808320938a16835292905220548511156128f857604051631e9acf1760e31b815260040160405180910390fd5b6001600160a01b038082166000908152600760209081526040808320938a168352929052908120805487929061292f908490614e42565b90915550506001600160a01b03878116600090815260096020526040902080546001600160a01b031916918316919091179055831561298f576001600160a01b038116600090815260186020526040902061298a9088613552565b6129b1565b6001600160a01b03811660009081526016602052604090206129b19088613552565b604080516001600160a01b0389811682526020820188905283168183015290517fbb19d9066294aca9bb81670382b302c69053c26acb490cfbb522b07ac2bb0b8b9181900360600190a150506004548114612a1e5760405162461bcd60e51b8152600401610d2b9061475d565b505050505050565b612a2e6135a4565b6001600160a01b0383166000908152600860205260409020805460ff19168315801591909117909155612ad45760405163095ea7b360e01b81526001600160a01b0384169063095ea7b390612a8b90849060001990600401614e96565b6020604051808303816000875af1158015612aaa573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ace9190614eaf565b50612b48565b60405163095ea7b360e01b81526001600160a01b0384169063095ea7b390612b03908490600090600401614e96565b6020604051808303816000875af1158015612b22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b469190614eaf565b505b604080516001600160a01b038581168252841515602083015283168183015290517f6faac0235cc96062eedf703b47b00eaa4680c2cfa3ffae5865f699137264f0159181900360600190a1505050565b612ba06135a4565b60118290558015612bb15780612bb3565b425b60125560408051838152602081018390527fddd57316dedc944a54a2d4ccbec17883e25f23eebb11ea1a4800f36502a469f0910160405180910390a15050565b6001600160a01b0381166000908152600c60205260409020606090610ef9908585613675565b612c216135a4565b6001600160a01b038116612c485760405163e6c4247b60e01b815260040160405180910390fd5b60058054610100600160a81b0319166101006001600160a01b038416021790556040517f9985022676a73860c32a3b91ea7a7dfe2d5e87c148f50eb519d8b0f33ab7f8b990610c9e908390613ed3565b612ca06135a4565b6001600160a01b038116612cc75760405163e6c4247b60e01b815260040160405180910390fd5b600e80546001600160a01b0319166001600160a01b0383161790556040517f63bb8d8b29c197c8b58de8e513ab743bce7bf84c87adc05f2cc18e609d5d0e8690610c9e908390613ed3565b6001600160a01b0381166000908152601760205260409020606090610ef9908585613675565b60055460ff1615612d815760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b6044820152606401610d2b565b6005805460ff19166001908117909155600455565b60035460ff1615612db95760405162461bcd60e51b8152600401610d2b906146a8565b600160046000828254612dcc919061471b565b90915550506004546001600160a01b03831660009081526008602052604090205433908490899060ff16612e135760405163621a135560e01b815260040160405180910390fd5b6001600160a01b03808416600090815260076020908152604080832093861683529290522054811115612e5957604051631e9acf1760e31b815260040160405180910390fd5b612e638383613934565b612e805760405163b79adbed60e01b815260040160405180910390fd5b612e908c8c8c8c8c8c8c8c6139ab565b5050506004548114612eb45760405162461bcd60e51b8152600401610d2b9061475d565b505050505050505050565b6001600160a01b038316600090815260136020526040902054606090819081908190851115612f04576001600160a01b03871660009081526013602052604090205494505b846001600160401b03811115612f1c57612f1c614ecc565b604051908082528060200260200182016040528015612f45578160200160208202803683370190505b509350846001600160401b03811115612f6057612f60614ecc565b604051908082528060200260200182016040528015612f89578160200160208202803683370190505b509150846001600160401b03811115612fa457612fa4614ecc565b604051908082528060200260200182016040528015612fcd578160200160208202803683370190505b509250846001600160401b03811115612fe857612fe8614ecc565b604051908082528060200260200182016040528015613011578160200160208202803683370190505b50905060005b8581101561312b576001600160a01b0388166000908152601360205260408120613041838a61471b565b815481106130515761305161472e565b6000918252602090912001546001600160a01b03169050613072818a613b90565b8584815181106130845761308461472e565b6020026020010185858151811061309d5761309d61472e565b602002602001018281525082151515158152505050808683815181106130c5576130c561472e565b6001600160a01b039283166020918202929092018101919091528282166000908152600782526040808220938d168252929091522054855186908490811061310f5761310f61472e565b60209081029190910101525061312481614744565b9050613017565b5093509350935093565b60035460ff16156131585760405162461bcd60e51b8152600401610d2b906146a8565b3361316b61014083016101208401613f79565b6001600160a01b03811660009081526008602052604090205460a08401359060ff166131aa5760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156131f057604051631e9acf1760e31b815260040160405180910390fd5b6131fa8383613934565b6132175760405163b79adbed60e01b815260040160405180910390fd5b60065460405163584ceab960e11b81526000916001600160a01b03169063b099d57290613248908890600401614f50565b6020604051808303816000875af1158015613267573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061328b919061497b565b6000818152600b60205260409081902080546001600160a01b0319163390811790915590519192507ff226e2c0a5c137f1fc5331d8dabe76ea9810e121bc9b18737125bcaa884daa8091611abe919060a0890135908590614e21565b60008215613379578183111561333c57600054613311906001600160a01b03868116911684613618565b61331b8284614e42565b90508015613337576133376001600160a01b0385168683613618565b613379565b6001600160a01b0380861660009081526007602090815260408083209388168352929052908120805485929061337390849061471b565b90915550505b949350505050565b81546000908103613394575060006133f3565b6001600160a01b0382166000908152600184016020526040902054801515806133ef5750826001600160a01b0316846000016000815481106133d8576133d861472e565b6000918252602090912001546001600160a01b0316145b9150505b92915050565b6134038282613381565b6134455760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b6044820152606401610d2b565b6001600160a01b038116600090815260018084016020526040822054845490929161346f91614e42565b90508082146134fb57600084600001828154811061348f5761348f61472e565b60009182526020909120015485546001600160a01b03909116915081908690859081106134be576134be61472e565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b835484908061350c5761350c615071565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b61355c8282613381565b610ecf5781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555050565b6000546001600160a01b031633146136165760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610d2b565b565b61367083846001600160a01b031663a9059cbb858560405160240161363e929190614e96565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613d7b565b505050565b60606000613683838561471b565b8554909150811115613693575083545b8381116136b0575050604080516000815260208101909152610efc565b60006136bc8583614e42565b90506000816001600160401b038111156136d8576136d8614ecc565b604051908082528060200260200182016040528015613701578160200160208202803683370190505b50905060005b82811015613784578761371a888361471b565b8154811061372a5761372a61472e565b9060005260206000200160009054906101000a90046001600160a01b031682828151811061375a5761375a61472e565b6001600160a01b03909216602092830291909101909101528061377c81614744565b915050613707565b509695505050505050565b6001600160a01b03821660009081526008602052604090205460ff166137c85760405163621a135560e01b815260040160405180910390fd5b6001600160a01b038084166000908152600760209081526040808320938616808452939091528082205490516370a0823160e01b81529092906370a0823190613815903090600401613ed3565b602060405180830381865afa158015613832573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613856919061497b565b90506000821180156138685750818110155b15613881576138816001600160a01b0385168484613618565b6001600160a01b038086166000908152600760209081526040808320938816835292815282822082905560139052206138ba9086613381565b156138e1576001600160a01b03841660009081526013602052604090206138e190866133f9565b604080516001600160a01b0380881682528087166020830152851691810191909152606081018390527f5aecd162bb2e2f8d70294fe483d6a8596cf8555a650f08a962220feaba5c21fe90608001611abe565b6001600160a01b038083166000908152601060209081526040808320938516835292905290812054421080610efc57506001600160a01b03808416600090815260106020908152604080832093861683529290522054158015610efc5750426011546012546139a3919061471b565b119392505050565b3360009081526007602090815260408083206001600160a01b0386168452909152812080548892906139de908490614e42565b909155506000905060ff821615613a7e576005546040516349d8615760e11b81526101009091046001600160a01b0316906393b0c2ae90613a34908c908c908c908c908c908c908c906000908d90600401615087565b6020604051808303816000875af1158015613a53573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a77919061464d565b9050613b06565b6005546040516214e17b60e41b81526101009091046001600160a01b03169063014e17b090613ac0908c908c908c908c908c908c908c906000906004016150e3565b6020604051808303816000875af1158015613adf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b03919061464d565b90505b6001600160a01b038116600090815260096020908152604080832080546001600160a01b031916339081179091558352600c9091529020613b479082613552565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838188336000604051613b7d9493929190614e55565b60405180910390a1505050505050505050565b6001600160a01b038116600090815260086020526040812054819060ff168015613bde57506001600160a01b0380851660009081526007602090815260408083209387168352929052205415155b15613c6c576001600160a01b038085166000908152601060209081526040808320938716835292905290812054613c2457601154601254613c1f919061471b565b613c4b565b6001600160a01b038086166000908152601060209081526040808320938816835292905220545b9050428111925082613c5e576000613c68565b613c684282614e42565b9150505b9250929050565b61189484856001600160a01b03166323b872dd86868660405160240161363e9392919061466a565b6001600160a01b0383166000908152601360205260409020613cbd9085613552565b6001600160a01b03808516600090815260076020908152604080832093871683529290529081208054849290613cf490849061471b565b9091555050601154613d06904261471b565b6001600160a01b03858116600081815260106020908152604080832089861680855290835292819020959095558451928352820152918201849052821660608201527ff462a3a00cd477c2aa6252dfbff27a7cf55ad6f75c526f4650b5bb92ef2912689060800160405180910390a150505050565b6000613d906001600160a01b03841683613dd5565b90508051600014158015613db5575080806020019051810190613db39190614eaf565b155b156136705782604051635274afe760e01b8152600401610d2b9190613ed3565b6060610efc8383600084600080856001600160a01b03168486604051613dfb9190615134565b60006040518083038185875af1925050503d8060008114613e38576040519150601f19603f3d011682016040523d82523d6000602084013e613e3d565b606091505b5091509150613e4d868383613e57565b9695505050505050565b606082613e6c57613e6782613eaa565b610efc565b8151158015613e8357506001600160a01b0384163b155b15613ea35783604051639996b31560e01b8152600401610d2b9190613ed3565b5080610efc565b805115613eba5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0391909116815260200190565b6001600160a01b0381168114610eb057600080fd5b8035613f0781613ee7565b919050565b8015158114610eb057600080fd5b600080600080600060a08688031215613f3257600080fd5b8535613f3d81613ee7565b945060208601359350604086013592506060860135613f5b81613ee7565b91506080860135613f6b81613f0c565b809150509295509295909350565b600060208284031215613f8b57600080fd5b8135610efc81613ee7565b600080600060608486031215613fab57600080fd5b8335613fb681613ee7565b92506020840135613fc681613ee7565b929592945050506040919091013590565b600060208284031215613fe957600080fd5b8135610efc81613f0c565b6000806040838503121561400757600080fd5b823561401281613ee7565b946020939093013593505050565b60008060006060848603121561403557600080fd5b8335925060208401359150604084013561404e81613ee7565b809150509250925092565b600081518084526020808501945080840160005b838110156140925781516001600160a01b03168752958201959082019060010161406d565b509495945050505050565b602081526000610efc6020830184614059565b60008083601f8401126140c257600080fd5b5081356001600160401b038111156140d957600080fd5b6020830191508360208260051b8501011115613c6c57600080fd5b6000806000806060858703121561410a57600080fd5b84356001600160401b0381111561412057600080fd5b61412c878288016140b0565b909550935050602085013561414081613ee7565b9150604085013561415081613ee7565b939692955090935050565b60006020828403121561416d57600080fd5b81356001600160401b0381111561418357600080fd5b82016101008185031215610efc57600080fd5b6000602082840312156141a857600080fd5b5035919050565b600080600080600080600060c0888a0312156141ca57600080fd5b87356001600160401b038111156141e057600080fd5b6141ec8a828b016140b0565b909850965050602088013594506040880135935060608801359250608088013561421581613ee7565b915060a088013561422581613ee7565b8091505092959891949750929550565b6000610140828403121561424857600080fd5b50919050565b60008060006040848603121561426357600080fd5b83356001600160401b0381111561427957600080fd5b614285868287016140b0565b909450925050602084013561404e81613ee7565b6000806000606084860312156142ae57600080fd5b83356142b981613ee7565b925060208401356142c981613ee7565b9150604084013561404e81613ee7565b6000602082840312156142eb57600080fd5b81356001600160401b0381111561430157600080fd5b820160c08185031215610efc57600080fd5b6000806000806080858703121561432957600080fd5b84359350602085013561433b81613ee7565b925060408501359150606085013561415081613ee7565b60008060006060848603121561436757600080fd5b833561437281613ee7565b95602085013595506040909401359392505050565b6000806040838503121561439a57600080fd5b82356143a581613ee7565b915060208301356143b581613ee7565b809150509250929050565b600080600080606085870312156143d657600080fd5b84356001600160401b038111156143ec57600080fd5b6143f8878288016140b0565b909550935050602085013561440c81613ee7565b9396929550929360400135925050565b600080600080600060a0868803121561443457600080fd5b85359450602086013561444681613ee7565b9350604086013561445681613ee7565b9250606086013591506080860135613f6b81613f0c565b60008060006060848603121561448257600080fd5b833561448d81613ee7565b925060208401356142c981613f0c565b600080604083850312156144b057600080fd5b50508035926020909101359150565b803560ff81168114613f0757600080fd5b60008060008060008060008060e0898b0312156144ec57600080fd5b88356001600160401b0381111561450257600080fd5b61450e8b828c016140b0565b909950975050602089013595506040890135945060608901359350608089013561453781613ee7565b925060a089013561454781613ee7565b915061455560c08a016144bf565b90509295985092959890939650565b600081518084526020808501945080840160005b8381101561409257815187529582019590820190600101614578565b6080815260006145a76080830187614059565b6020838203818501526145ba8288614564565b8481036040860152865180825282880193509082019060005b818110156145f15784511515835293830193918301916001016145d3565b505084810360608601526146058187614564565b9998505050505050505050565b60006020828403121561462457600080fd5b81356001600160401b0381111561463a57600080fd5b82016101608185031215610efc57600080fd5b60006020828403121561465f57600080fd5b8151610efc81613ee7565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0392831681529116602082015260400190565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b634e487b7160e01b600052601160045260246000fd5b808201808211156133f3576133f3614705565b634e487b7160e01b600052603260045260246000fd5b60006001820161475657614756614705565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526016908201527529b832b2b226b0b935b2ba39a0a6a6a1b932b0ba37b960511b604082015260600190565b6000808335601e198436030181126147db57600080fd5b8301803591506001600160401b038211156147f557600080fd5b6020019150600581901b3603821315613c6c57600080fd5b80356001600160401b0381168114613f0757600080fd5b6000808335601e1984360301811261483b57600080fd5b83016020810192503590506001600160401b0381111561485a57600080fd5b8060051b3603821315613c6c57600080fd5b6002811061488a57634e487b7160e01b600052602160045260246000fd5b9052565b803560028110613f0757600080fd5b6000602080835261012083018435828501526001600160401b036148c283870161480d565b16604085015260408501356060850152606085013560808501526148e96080860186614824565b61010060a087018190529281905290916000919061014087015b8284101561492f5761491d816149188761488e565b61486c565b93850193600193909301928501614903565b61493b60a08a01613efc565b6001600160a01b03811660c08a0152955060c089013560e089015261496260e08a01613efc565b6001600160a01b03169190970152509395945050505050565b60006020828403121561498d57600080fd5b5051919050565b6000602082840312156149a657600080fd5b610efc8261480d565b8135815261014081016149c46020840161480d565b6001600160401b031660208301526149de6040840161480d565b6001600160401b0381166040840152506060830135606083015260808301356080830152614a0e60a0840161488e565b614a1b60a084018261486c565b50614a2860c08401613efc565b6001600160a01b031660c083015260e08381013590830152610100614a4e818501613efc565b6001600160a01b03811684830152505061012092830135919092015290565b600060208284031215614a7f57600080fd5b610efc8261488e565b6001600160a01b03871681526020810186905260408101859052606081018490526001600160401b038316608082015260c08101614ac960a083018461486c565b979650505050505050565b803561ffff81168114613f0757600080fd5b8035600281900b8114613f0757600080fd5b803562ffffff81168114613f0757600080fd5b81835260006001600160fb1b03831115614b2457600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b858110156140925761ffff614b6183614ad4565b16875260ff614b718484016144bf565b16838801526040614b83818401614ae6565b60020b908801526060968701969190910190600101614b4d565b81835260006020808501808196508560051b810191508460005b87811015614c245782840389528135601e19883603018112614bd857600080fd5b870185810190356001600160401b03811115614bf357600080fd5b606081023603821315614c0557600080fd5b614c10868284614b3d565b9a87019a9550505090840190600101614bb7565b5091979650505050505050565b6000823561015e19833603018112614c4857600080fd5b90910192915050565b81835260006020808501808196508560051b810191508460005b87811015614c24578284038952614c828288614c31565b61016081358652614c94878301614ad4565b61ffff16878701526040614ca9838201614ad4565b61ffff1690870152606082810135908701526080614cc88184016144bf565b60ff169087015260a0614cdc838201614ae6565b614cea8289018260020b9052565b505060c0614cf9818401614af8565b62ffffff169087015260e0614d1083820184614824565b83838a0152614d22848a018284614b0b565b9350505050610100614d3681840184614824565b888403838a0152614d48848284614b0b565b9350505050610120614d5b8184016144bf565b60ff1690870152610140614d7183820184614824565b935087830382890152614d85838583614b9d565b9c89019c97505050928601925050600101614c6b565b602081526000614dab8384614824565b60c06020850152614dc060e085018284614c51565b9150506020840135604084015260408401356060840152606084013560808401526080840135614def81613ee7565b6001600160a01b0390811660a08581019190915285013590614e1082613ee7565b1660c0939093019290925250919050565b6001600160a01b039390931683526020830191909152604082015260600190565b818103818111156133f3576133f3614705565b6001600160a01b039485168152602081019390935292166040820152901515606082015260800190565b80820281158282048414176133f3576133f3614705565b6001600160a01b03929092168252602082015260400190565b600060208284031215614ec157600080fd5b8151610efc81613f0c565b634e487b7160e01b600052604160045260246000fd5b6000808335601e19843603018112614ef957600080fd5b83016020810192503590506001600160401b03811115614f1857600080fd5b803603821315613c6c57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000614f608384614ee2565b610160806020860152614f7861018086018385614f27565b9250614f8660208701614ad4565b61ffff811660408701529150614f9e60408701614ad4565b61ffff811660608701529150614fb660608701614ae6565b9150614fc7608086018360020b9052565b614fd3608087016144bf565b60ff811660a0870152915060a086013560c086015260c086013560e0860152610100915060e08601358286015261500b828701613efc565b9150610120615024818701846001600160a01b03169052565b61502f818801613efc565b925050610140615049818701846001600160a01b03169052565b615054818801614af8565b9250506150678186018362ffffff169052565b5090949350505050565b634e487b7160e01b600052603160045260246000fd5b600061010080835261509c8184018c8e614c51565b602084019a909a525050604081019690965260608601949094526001600160a01b039283166080860152911660a0840152151560c083015260ff1660e09091015292915050565b60e0815260006150f760e083018a8c614c51565b602083019890985250604081019590955260608501939093526001600160a01b0391821660808501521660a0830152151560c09091015292915050565b6000825160005b81811015615155576020818601810151858301520161513b565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca26469706673582212203e2b794f930d8c1d4f38ca80981a7f46737630b7b494a6e4a25994de068b6eea64736f6c63430008140033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$49,402.71
Net Worth in POL
Token Allocations
USDC
100.00%
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| BASE | 100.00% | $0.999939 | 49,405.7198 | $49,402.71 |
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.