Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
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 "../../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;
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;
/* ========== 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 {
require(supportedCollateral[_collateral], "Unsupported collateral");
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 {
require(supportedCollateral[_collateral], "Unsupported collateral");
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) {
require(
(freeBetExpiration[_user][_collateral] > 0 && freeBetExpiration[_user][_collateral] < block.timestamp) ||
(freeBetExpiration[_user][_collateral] == 0 &&
freeBetExpirationUpgrade + freeBetExpirationPeriod < block.timestamp),
"Free bet not expired"
);
_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 {
require(supportedCollateral[_collateral], "Unsupported collateral");
for (uint256 index; index < _users.length; ++index) {
address _user = _users[index];
_removeUserFunding(_user, _collateral, _receiver);
}
}
function _removeUserFunding(address _user, address _collateral, address _receiver) internal {
require(supportedCollateral[_collateral], "Unsupported collateral");
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 {
require(msg.sender == address(liveTradingProcessor), "Only callable from LiveTradingProcessor");
address _user = liveRequestsPerUser[requestId];
require(_user != address(0), "Unknown live ticket");
if (_collateral == address(0)) {
_collateral = address(sportsAMM.defaultCollateral());
}
require(supportedCollateral[_collateral], "Unsupported collateral");
require(balancePerUserAndCollateral[_user][_collateral] >= _buyInAmount, "Insufficient balance");
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 {
require(msg.sender == address(sgpTradingProcessor), "Only callable from SGPTradingProcessor");
address _user = sgpRequestsPerUser[requestId];
require(_user != address(0), "Unknown SGP ticket");
if (_collateral == address(0)) {
_collateral = address(sportsAMM.defaultCollateral());
}
require(supportedCollateral[_collateral], "Unsupported collateral");
require(balancePerUserAndCollateral[_user][_collateral] >= _buyInAmount, "Insufficient balance");
balancePerUserAndCollateral[_user][_collateral] -= _buyInAmount;
ticketToUser[_createdTicket] = _user;
activeTicketsPerUser[_user].add(_createdTicket);
emit FreeBetTrade(_createdTicket, _buyInAmount, _user, true);
}
/// @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 {
require(msg.sender == address(sportsAMM), "Only allowed from SportsAMM");
address _user = ticketToUser[_resolvedTicket];
require(_user != address(0), "Unknown ticket");
require(activeTicketsPerUser[_user].contains(_resolvedTicket), "Unknown active ticket");
uint _exercized = Ticket(_resolvedTicket).finalPayout();
uint _earned;
if (_exercized > 0) {
IERC20 _collateral = Ticket(_resolvedTicket).collateral();
uint buyInAmount = Ticket(_resolvedTicket).buyInAmount();
if (_exercized > buyInAmount) {
_collateral.safeTransfer(owner, buyInAmount);
_earned = _exercized - buyInAmount;
if (_earned > 0) {
_collateral.safeTransfer(_user, _earned);
}
} else {
balancePerUserAndCollateral[_user][address(_collateral)] += _exercized;
}
}
emit FreeBetTicketResolved(_resolvedTicket, _user, _earned);
activeTicketsPerUser[_user].remove(_resolvedTicket);
resolvedTicketsPerUser[_user].add(_resolvedTicket);
}
/// @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
function addSupportedCollateral(address _collateral, bool _supported) external onlyOwner {
supportedCollateral[_collateral] = _supported;
if (_supported) {
IERC20(_collateral).approve(address(sportsAMM), MAX_APPROVAL);
} else {
IERC20(_collateral).approve(address(sportsAMM), 0);
}
emit CollateralSupportChanged(_collateral, _supported);
}
/* ========== 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 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 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 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 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 {
require(_liveTradingProcessor != address(0), "Invalid address");
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 {
require(_sgpTradingProcessor != address(0), "Invalid address");
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 {
require(_sportsAMM != address(0), "Invalid address");
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]);
}
}
/* ========== INTERNAL FUNCTIONS ========== */
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
) {
require(supportedCollateral[_collateral], "Unsupported collateral");
require(balancePerUserAndCollateral[_user][_collateral] >= _amount, "Insufficient balance");
require(_isFreeBetValid(_user, _collateral), "Free bet expired");
_;
}
/* ========== EVENTS ========== */
event SetSportsAMM(address sportsAMM);
event SetLiveTradingProcessor(address liveTradingProcessor);
event SetSGPTradingProcessor(address sgpTradingProcessor);
event UserFunded(address user, address collateral, uint amount, address funder);
event FreeBetTrade(address createdTicket, uint buyInAmount, address user, bool isLive);
event CollateralSupportChanged(address collateral, bool supported);
event FreeBetTicketResolved(address ticket, address user, uint earned);
event FreeBetLiveTradeRequested(address user, uint buyInAmount, bytes32 requestId);
event FreeBetSGPTradeRequested(address user, uint buyInAmount, bytes32 requestId);
event UserFundingRemoved(address _user, address _collateral, address _receiver, uint _amount);
event SetFreeBetExpirationPeriod(uint freeBetExpirationPeriod, uint freeBetExpirationUpgrade);
}// 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.8.20;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// internal
import "../../interfaces/ISportsAMMV2Manager.sol";
import "../../interfaces/ISportsAMMV2.sol";
contract Ticket {
using SafeERC20 for IERC20;
uint private constant ONE = 1e18;
enum Phase {
Trading,
Maturity,
Expiry
}
struct MarketData {
bytes32 gameId;
uint16 sportId;
uint16 typeId;
uint maturity;
uint8 status;
int24 line;
uint24 playerId;
uint8 position;
uint odd;
ISportsAMMV2.CombinedPosition[] combinedPositions;
}
struct TicketInit {
MarketData[] _markets;
uint _buyInAmount;
uint _fees;
uint _totalQuote;
address _sportsAMM;
address _ticketOwner;
IERC20 _collateral;
uint _expiry;
bool _isLive;
uint8 _systemBetDenominator;
bool _isSGP;
}
ISportsAMMV2 public sportsAMM;
address public ticketOwner;
IERC20 public collateral;
uint public buyInAmount;
uint public fees;
uint public totalQuote;
uint public numOfMarkets;
uint public expiry;
uint public createdAt;
bool public resolved;
bool public paused;
bool public initialized;
bool public cancelled;
bool public isLive;
mapping(uint => MarketData) public markets;
uint public finalPayout;
bool public isSystem;
uint8 public systemBetDenominator;
bool public isSGP;
bool public isMarkedAsLost;
uint public expectedFinalPayout;
/* ========== CONSTRUCTOR and INITIALIZERS========== */
/// @notice initialize the ticket contract
/// @param params all parameters for Init
function initialize(TicketInit calldata params) external {
require(!initialized, "Ticket already initialized");
initialized = true;
sportsAMM = ISportsAMMV2(params._sportsAMM);
numOfMarkets = params._markets.length;
for (uint i = 0; i < numOfMarkets; i++) {
markets[i] = params._markets[i];
}
buyInAmount = params._buyInAmount;
fees = params._fees;
totalQuote = params._totalQuote;
ticketOwner = params._ticketOwner;
collateral = params._collateral;
expiry = params._expiry;
isLive = params._isLive;
createdAt = block.timestamp;
systemBetDenominator = params._systemBetDenominator;
isSystem = systemBetDenominator > 0;
isSGP = params._isSGP;
}
/**
* @notice Sets the expected final payout amount for this ticket.
* @dev
* - Can only be called by the SportsAMM contract.
* - This value represents the total amount of collateral (including fees)
* that was initially funded to the ticket upon creation.
* - Used later in `exercise()` to prevent manipulation or overfunding attacks,
* ensuring payout calculations rely only on the original committed collateral
* and not on the current token balance of the contract.
* - Once set, this value should remain constant throughout the ticket lifecycle.
*
* @param amount The total expected collateral amount that should be held by this ticket.
* Must include both user buy-in and fees.
*
* Emits a {ExpectedFinalPayoutSet} event.
*/
function setExpectedFinalPayout(uint amount) external onlyAMM {
expectedFinalPayout = amount;
emit ExpectedFinalPayoutSet(amount);
}
/* ========== EXTERNAL READ FUNCTIONS ========== */
/// @notice checks if the user lost the ticket
/// @return isTicketLost true/false
function isTicketLost() public view returns (bool) {
if (isMarkedAsLost) {
return true;
} else {
uint lostMarketsCount = 0;
for (uint i = 0; i < numOfMarkets; i++) {
(bool isMarketResolved, bool isWinningMarketPosition) = sportsAMM
.resultManager()
.isMarketResolvedAndPositionWinning(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
if (isMarketResolved && !isWinningMarketPosition) {
if (!isSystem) {
return true;
} else {
lostMarketsCount++;
if (lostMarketsCount > (numOfMarkets - systemBetDenominator)) {
return true;
}
}
}
}
return false;
}
}
/// @notice checks are all markets of the ticket resolved
/// @return areAllMarketsResolved true/false
function areAllMarketsResolved() public view returns (bool) {
for (uint i = 0; i < numOfMarkets; i++) {
if (
!sportsAMM.resultManager().isMarketResolved(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].combinedPositions
)
) {
return false;
}
}
return true;
}
/// @notice checks if the user won the ticket
/// @return hasUserWon true/false
function isUserTheWinner() external view returns (bool hasUserWon) {
hasUserWon = _isUserTheWinner();
}
/// @notice checks if the ticket ready to be exercised
/// @return isExercisable true/false
function isTicketExercisable() public view returns (bool isExercisable) {
isExercisable = !resolved && (areAllMarketsResolved() || isTicketLost());
}
/// @notice gets current phase of the ticket
/// @return phase ticket phase
function phase() public view returns (Phase) {
return
isTicketExercisable() || resolved ? ((expiry < block.timestamp) ? Phase.Expiry : Phase.Maturity) : Phase.Trading;
}
/// @notice gets combined positions of the game
/// @return combinedPositions game combined positions
function getCombinedPositions(
uint _marketIndex
) public view returns (ISportsAMMV2.CombinedPosition[] memory combinedPositions) {
return markets[_marketIndex].combinedPositions;
}
/// @notice return the payout for this ticket
/// @return systemBetPayout the payout for this ticket
function getSystemBetPayout() external view returns (uint systemBetPayout) {
systemBetPayout = _getSystemBetPayout();
}
/* ========== EXTERNAL WRITE FUNCTIONS ========== */
/// @notice exercise ticket
function exercise(address _exerciseCollateral) external onlyAMM notPaused returns (uint) {
bool isExercisable = isTicketExercisable();
require(isExercisable, "Ticket not exercisable yet");
require(expectedFinalPayout > 0, "Expected final payout not set");
uint payoutWithFees = expectedFinalPayout;
uint payout = payoutWithFees - fees;
bool isCancelled = false;
if (_isUserTheWinner()) {
finalPayout = payout;
isCancelled = true;
for (uint i = 0; i < numOfMarkets; i++) {
bool isCancelledMarketPosition = sportsAMM.resultManager().isCancelledMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
if (isCancelledMarketPosition) {
if (isSGP) {
isCancelled = true;
break;
}
finalPayout = (finalPayout * markets[i].odd) / ONE;
} else {
isCancelled = false;
}
}
finalPayout = isCancelled ? buyInAmount : (isSystem ? _getSystemBetPayout() : finalPayout);
collateral.safeTransfer(
_exerciseCollateral == address(0) || _exerciseCollateral == address(collateral)
? address(ticketOwner)
: address(sportsAMM),
finalPayout
);
}
// if user is lost or if the user payout was less than anticipated due to cancelled games, send the remainder to AMM
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(address(sportsAMM), balance);
}
_resolve(!isTicketLost(), isCancelled);
return finalPayout;
}
/// @notice expire ticket
function expire(address _beneficiary) external onlyAMM {
require(phase() == Phase.Expiry, "Ticket not in expiry phase");
require(!resolved, "Can't expire resolved ticket");
emit Expired(_beneficiary);
_selfDestruct(_beneficiary);
}
/// @notice cancel the ticket
function cancel() external onlyAMM notPaused returns (uint) {
finalPayout = buyInAmount;
collateral.safeTransfer(address(ticketOwner), finalPayout);
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(address(sportsAMM), balance);
}
_resolve(true, true);
return finalPayout;
}
/// @notice mark the ticket as lost
function markAsLost() external onlyAMM notPaused returns (uint) {
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(address(sportsAMM), balance);
}
_resolve(false, false);
isMarkedAsLost = true;
return 0;
}
/// @notice withdraw collateral from the ticket
function withdrawCollateral(address recipient) external onlyAMM {
collateral.safeTransfer(recipient, collateral.balanceOf(address(this)));
}
/* ========== INTERNAL FUNCTIONS ========== */
function _resolve(bool _hasUserWon, bool _cancelled) internal {
resolved = true;
cancelled = _cancelled;
emit Resolved(_hasUserWon, _cancelled);
}
function _selfDestruct(address beneficiary) internal {
uint balance = collateral.balanceOf(address(this));
if (balance != 0) {
collateral.safeTransfer(beneficiary, balance);
}
}
function _isUserTheWinner() internal view returns (bool hasUserWon) {
if (areAllMarketsResolved()) {
hasUserWon = !isTicketLost();
}
}
/* ========== SETTERS ========== */
function setPaused(bool _paused) external {
require(msg.sender == address(sportsAMM.manager()), "Invalid sender");
if (paused == _paused) return;
paused = _paused;
emit PauseUpdated(_paused);
}
/* ========== SYSTEM BET UTILS ========== */
function _getSystemBetPayout() internal view returns (uint systemBetPayout) {
if (isSystem) {
uint8[][] memory systemCombinations = sportsAMM.riskManager().generateCombinations(
uint8(numOfMarkets),
systemBetDenominator
);
uint totalCombinations = systemCombinations.length;
uint buyinPerCombination = ((buyInAmount * ONE) / totalCombinations) / ONE;
bool[] memory winningMarkets = new bool[](numOfMarkets);
bool[] memory cancelledMarkets = new bool[](numOfMarkets);
for (uint i = 0; i < numOfMarkets; i++) {
if (
!sportsAMM.resultManager().isMarketResolved(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].combinedPositions
)
) {
return 0;
}
winningMarkets[i] = sportsAMM.resultManager().isWinningMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
cancelledMarkets[i] = sportsAMM.resultManager().isCancelledMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
}
// Loop through each stored combination
for (uint i = 0; i < totalCombinations; i++) {
uint8[] memory currentCombination = systemCombinations[i];
uint combinationQuote = ONE;
for (uint j = 0; j < currentCombination.length; j++) {
uint8 marketIndex = currentCombination[j];
if (winningMarkets[marketIndex]) {
if (!cancelledMarkets[marketIndex]) {
combinationQuote = (combinationQuote * markets[marketIndex].odd) / ONE;
}
} else {
combinationQuote = 0;
break;
}
}
if (combinationQuote > 0) {
uint combinationPayout = (buyinPerCombination * ONE) / combinationQuote;
systemBetPayout += combinationPayout;
}
}
uint maxPayout = (buyInAmount * ONE) / totalQuote;
if (systemBetPayout > maxPayout) {
systemBetPayout = maxPayout;
}
}
}
/* ========== MODIFIERS ========== */
modifier onlyAMM() {
require(msg.sender == address(sportsAMM), "Only the AMM may perform these methods");
_;
}
modifier notPaused() {
require(!paused, "Market paused");
_;
}
/* ========== EVENTS ========== */
event Resolved(bool isUserTheWinner, bool cancelled);
event Expired(address beneficiary);
event PauseUpdated(bool paused);
event ExpectedFinalPayoutSet(uint amount);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IProxyBetting.sol";
interface IFreeBetsHolder is IProxyBetting {
function confirmLiveTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount, address _collateral) external;
function confirmSGPTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount, address _collateral) external;
function balancePerUserAndCollateral(address user, address collateral) external view returns (uint);
function freeBetExpiration(address user, address collateral) external view returns (uint);
function freeBetExpirationUpgrade() external view returns (uint);
function freeBetExpirationPeriod() external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface 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 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.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":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"bool","name":"supported","type":"bool"}],"name":"CollateralSupportChanged","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":"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":"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":"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"}],"name":"addSupportedCollateral","outputs":[],"stateMutability":"nonpayable","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":"_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":"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":"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":"numOfActiveTicketsPerUser","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":"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":[],"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":"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":"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
608060405234801561001057600080fd5b506140a7806100206000396000f3fe608060405234801561001057600080fd5b50600436106102c15760003560e01c80639168fdd11161017d578063cab7bf56116100d9578063ebc7977211610092578063ebc79772146106ef578063edc5de8e146106f7578063efac480d14610700578063f38329d914610713578063fa550eba14610736578063fd73489814610759578063fecc198b1461076c57600080fd5b8063cab7bf561461064d578063dd1d977314610678578063dd37861d1461068b578063e74e33e01461069e578063e81e52ee146106c9578063e94cdf5f146106dc57600080fd5b8063aba84bb111610136578063aba84bb1146105b6578063b1b5823b146105e0578063be899c89146105e9578063bea4ae20146105fc578063c0c53b8b1461060f578063c3b83f5f14610622578063c99252881461063557600080fd5b80639168fdd11461053857806391b4ded914610561578063924fbde11461056a57806394bd23131461057d578063a12d5b0714610590578063a13d6b6a146105a357600080fd5b806342d851ed1161022c57806353a47bb7116101e557806353a47bb71461049e5780635c975abb146104b157806379ba5097146104ce5780637eda2d7b146104d65780638da5cb5b146104e95780638f974cd9146104fc57806390e3afb21461052557600080fd5b806342d851ed1461040957806344cab7ee14610432578063485b23bf146104455780634d1ac474146104655780634fa98ca51461047857806352aa17a21461048b57600080fd5b806316c38b3c1161027e57806316c38b3c14610381578063188aa45d146103945780631a10bd76146103a7578063275fd247146103ba5780633a572121146103cd5780633a687e2b146103f657600080fd5b806304de96c5146102c6578063084a2fa4146102ef57806311e9c08d1461030457806313af4035146103325780631590a4a4146103455780631627540c1461036e575b600080fd5b6006546102d9906001600160a01b031681565b6040516102e691906130f5565b60405180910390f35b6103026102fd36600461312e565b610795565b005b61032461031236600461316f565b600a6020526000908152604090205481565b6040519081526020016102e6565b61030261034036600461316f565b6107c9565b61032461035336600461316f565b6001600160a01b03166000908152600c602052604090205490565b61030261037c36600461316f565b6108e5565b61030261038f36600461319a565b610938565b6103026103a23660046131b7565b6109aa565b6103026103b53660046131e3565b6109ca565b6103026103c8366004613260565b610b4c565b6102d96103db3660046132c7565b600b602052600090815260409020546001600160a01b031681565b6103026104043660046132e0565b610c45565b6102d96104173660046132c7565b600f602052600090815260409020546001600160a01b031681565b600e546102d9906001600160a01b031681565b610458610453366004613366565b610d6b565b6040516102e691906133e3565b6103026104733660046133f6565b610d9b565b610302610486366004613441565b610f6f565b610302610499366004613481565b610fe1565b6001546102d9906001600160a01b031681565b6003546104be9060ff1681565b60405190151581526020016102e6565b61030261119f565b6103026104e43660046134bb565b611277565b6000546102d9906001600160a01b031681565b6102d961050a36600461316f565b6009602052600090815260409020546001600160a01b031681565b6103026105333660046133f6565b61155b565b61032461054636600461316f565b6001600160a01b03166000908152600d602052604090205490565b61032460025481565b6104586105783660046134fa565b6115c6565b61030261058b36600461316f565b6115ec565b61030261059e3660046134bb565b611665565b6103026105b136600461316f565b611768565b6105c96105c436600461352f565b611acc565b6040805192151583526020830191909152016102e6565b61032460125481565b6103026105f736600461312e565b611ae4565b61030261060a36600461355d565b611b7b565b61030261061d366004613441565b611c65565b61030261063036600461316f565b611dbb565b6005546102d99061010090046001600160a01b031681565b61032461065b36600461352f565b600760209081526000928352604080842090915290825290205481565b6103026106863660046135b9565b611e89565b610458610699366004613366565b611edd565b6103246106ac36600461352f565b601060209081526000928352604080842090915290825290205481565b6103026106d736600461316f565b611f03565b6103026106ea36600461316f565b611f81565b610302611ffa565b61032460115481565b61030261070e3660046135ec565b612058565b6107266107213660046134fa565b61217e565b6040516102e694939291906136b0565b6104be61074436600461316f565b60086020526000908152604090205460ff1681565b61030261076736600461372e565b6123f4565b61032461077a36600461316f565b6001600160a01b031660009081526013602052604090205490565b61079d6125a3565b6001600160a01b0392831660009081526010602090815260408083209490951682529290925291902055565b6001600160a01b0381166108205760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064015b60405180910390fd5b600154600160a01b900460ff161561088c5760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610817565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b0319909116178155604051600080516020614052833981519152916108da918490613769565b60405180910390a150565b6108ed6125a3565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906108da9083906130f5565b6109406125a3565b60035460ff161515811515146109a7576003805460ff191682151590811790915560ff161561096e57426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec5906020016108da565b50565b6109b26125a3565b6109c66001600160a01b0383163383612617565b5050565b6109d26125a3565b6001600160a01b0382166000908152600860205260409020805460ff19168215801591909117909155610a845760055460405163095ea7b360e01b81526001600160a01b038085169263095ea7b392610a3b926101009092049091169060001990600401613783565b6020604051808303816000875af1158015610a5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a7e919061379c565b50610b04565b60055460405163095ea7b360e01b81526001600160a01b038085169263095ea7b392610abf9261010090920490911690600090600401613783565b6020604051808303816000875af1158015610ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b02919061379c565b505b604080516001600160a01b038416815282151560208201527fb44b294feae8400b9db26080aaedb6cd054001acc015182a0d4216d9e0e7d49091015b60405180910390a15050565b60035460ff1615610b6f5760405162461bcd60e51b8152600401610817906137b9565b600160046000828254610b82919061382c565b9091555050600454610b926125a3565b6001600160a01b03831660009081526008602052604090205460ff16610bca5760405162461bcd60e51b81526004016108179061383f565b60005b84811015610c1c576000868683818110610be957610be961386f565b9050602002016020810190610bfe919061316f565b9050610c0b818686612674565b50610c1581613885565b9050610bcd565b506004548114610c3e5760405162461bcd60e51b81526004016108179061389e565b5050505050565b60035460ff1615610c685760405162461bcd60e51b8152600401610817906137b9565b600160046000828254610c7b919061382c565b90915550506004546001600160a01b03821660009081526008602052604090205433908390889060ff16610cc15760405162461bcd60e51b81526004016108179061383f565b6001600160a01b03808416600090815260076020908152604080832093861683529290522054811115610d065760405162461bcd60e51b8152600401610817906138d5565b610d108383612818565b610d2c5760405162461bcd60e51b815260040161081790613903565b610d3d8b8b8b8b8b8b8b6000612892565b5050506004548114610d615760405162461bcd60e51b81526004016108179061389e565b5050505050505050565b6001600160a01b0381166000908152600d60205260409020606090610d91908585612a77565b90505b9392505050565b60035460ff1615610dbe5760405162461bcd60e51b8152600401610817906137b9565b600160046000828254610dd1919061382c565b909155505060045460005b83811015610f47576000858583818110610df857610df861386f565b9050602002016020810190610e0d919061316f565b6001600160a01b0380821660009081526007602090815260408083209389168352929052205490915015610f36576001600160a01b0380821660009081526010602090815260408083209388168352929052205415801590610e9357506001600160a01b0380821660009081526010602090815260408083209388168352929052205442115b80610eda57506001600160a01b03808216600090815260106020908152604080832093881683529290522054158015610eda575042601154601254610ed8919061382c565b105b610f1d5760405162461bcd60e51b8152602060048201526014602482015273119c99594818995d081b9bdd08195e1c1a5c995960621b6044820152606401610817565b600054610f3690829086906001600160a01b0316612674565b50610f4081613885565b9050610ddc565b506004548114610f695760405162461bcd60e51b81526004016108179061389e565b50505050565b60035460ff1615610f925760405162461bcd60e51b8152600401610817906137b9565b600160046000828254610fa5919061382c565b9091555050600454610fb56125a3565b610fc0848484612674565b6004548114610f695760405162461bcd60e51b81526004016108179061389e565b60035460ff16156110045760405162461bcd60e51b8152600401610817906137b9565b3361101560c0830160a0840161316f565b6001600160a01b038116600090815260086020908152604090912054908401359060ff166110555760405162461bcd60e51b81526004016108179061383f565b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481111561109a5760405162461bcd60e51b8152600401610817906138d5565b6110a48383612818565b6110c05760405162461bcd60e51b815260040161081790613903565b600e546040516306fea14960e21b81526000916001600160a01b031690631bfa8524906110f1908890600401613c3c565b6020604051808303816000875af1158015611110573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111349190613cc2565b6000818152600f602090815260409182902080546001600160a01b0319163390811790915591519293507f2a107e8d4cba0aa559543eb234520741dac54e7db15d8a96afc8df0dcb8f7525926111909291890135908590613cdb565b60405180910390a15050505050565b6001546001600160a01b031633146112175760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610817565b60005460015460405160008051602061405283398151915292611248926001600160a01b0391821692911690613769565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b60035460ff161561129a5760405162461bcd60e51b8152600401610817906137b9565b6001600460008282546112ad919061382c565b9091555050600454600e546001600160a01b0316331461131e5760405162461bcd60e51b815260206004820152602660248201527f4f6e6c792063616c6c61626c652066726f6d2053475054726164696e6750726f60448201526531b2b9b9b7b960d11b6064820152608401610817565b6000858152600f60205260409020546001600160a01b0316806113785760405162461bcd60e51b8152602060048201526012602482015271155b9adb9bdddb8814d1d4081d1a58dad95d60721b6044820152606401610817565b6001600160a01b03831661140057600560019054906101000a90046001600160a01b03166001600160a01b031663aeb0f1646040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113fd9190613cfc565b92505b6001600160a01b03831660009081526008602052604090205460ff166114385760405162461bcd60e51b81526004016108179061383f565b6001600160a01b0380821660009081526007602090815260408083209387168352929052205484111561147d5760405162461bcd60e51b8152600401610817906138d5565b6001600160a01b038082166000908152600760209081526040808320938716835292905290812080548692906114b4908490613d19565b90915550506001600160a01b03858116600090815260096020908152604080832080546001600160a01b0319169486169485179055928252600c9052206114fb9086612b91565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b640308385858360016040516115319493929190613d2c565b60405180910390a1506004548114610c3e5760405162461bcd60e51b81526004016108179061389e565b6115636125a3565b60005b82811015610f69576115b68484838181106115835761158361386f565b9050602002016020810190611598919061316f565b6001600160a01b038416600090815260136020526040902090612b91565b6115bf81613885565b9050611566565b6001600160a01b0383166000908152601360205260409020606090610d91908484612a77565b6115f46125a3565b6001600160a01b03811661161a5760405162461bcd60e51b815260040161081790613d56565b600680546001600160a01b0319166001600160a01b0383161790556040517f8b5670b3bc5c520b7a3580c753ba88065a561ad52a99674196859b044ea20285906108da9083906130f5565b60035460ff16156116885760405162461bcd60e51b8152600401610817906137b9565b60016004600082825461169b919061382c565b90915550506004546006546001600160a01b0316331461170d5760405162461bcd60e51b815260206004820152602760248201527f4f6e6c792063616c6c61626c652066726f6d204c69766554726164696e67507260448201526637b1b2b9b9b7b960c91b6064820152608401610817565b6000858152600b60205260409020546001600160a01b0316806113785760405162461bcd60e51b8152602060048201526013602482015272155b9adb9bdddb881b1a5d99481d1a58dad95d606a1b6044820152606401610817565b60055461010090046001600160a01b031633146117c75760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c7920616c6c6f7765642066726f6d2053706f727473414d4d00000000006044820152606401610817565b6001600160a01b0380821660009081526009602052604090205416806118205760405162461bcd60e51b815260206004820152600e60248201526d155b9adb9bdddb881d1a58dad95d60921b6044820152606401610817565b6001600160a01b0381166000908152600c602052604090206118429083612be3565b6118865760405162461bcd60e51b8152602060048201526015602482015274155b9adb9bdddb881858dd1a5d99481d1a58dad95d605a1b6044820152606401610817565b6000826001600160a01b031663242a8a6b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ea9190613cc2565b905060008115611a4d576000846001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015611934573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119589190613cfc565b90506000856001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa15801561199a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119be9190613cc2565b905080841115611a0d576000546119e2906001600160a01b03848116911683612617565b6119ec8185613d19565b92508215611a0857611a086001600160a01b0383168685612617565b611a4a565b6001600160a01b03808616600090815260076020908152604080832093861683529290529081208054869290611a4490849061382c565b90915550505b50505b7f66c0cec01a971dcd7f2bb22f7cc4b244c8f9ee6deb5540b073d0f10b4539e8b5848483604051611a8093929190613d7f565b60405180910390a16001600160a01b0383166000908152600c60205260409020611aaa9085612c59565b6001600160a01b0383166000908152600d60205260409020610f699085612b91565b600080611ad98484612db2565b909590945092505050565b60035460ff1615611b075760405162461bcd60e51b8152600401610817906137b9565b600160046000828254611b1a919061382c565b90915550506004546001600160a01b03831660009081526008602052604090205460ff16611b5a5760405162461bcd60e51b81526004016108179061383f565b611b6f6001600160a01b038416333085612e95565b610fc084848433612ebd565b60035460ff1615611b9e5760405162461bcd60e51b8152600401610817906137b9565b600160046000828254611bb1919061382c565b90915550506004546001600160a01b03831660009081526008602052604090205460ff16611bf15760405162461bcd60e51b81526004016108179061383f565b611c123330611c008786613da3565b6001600160a01b038716929190612e95565b60005b84811015610c1c576000868683818110611c3157611c3161386f565b9050602002016020810190611c46919061316f565b9050611c5481868633612ebd565b50611c5e81613885565b9050611c15565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015611caa5750825b90506000826001600160401b03166001148015611cc65750303b155b905081158015611cd4575080155b15611cf25760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611d1c57845460ff60401b1916600160401b1785555b611d25886107c9565b611d2d611ffa565b60058054610100600160a81b0319166101006001600160a01b038a81169190910291909117909155600680546001600160a01b0319169188169190911790558315610d6157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050505050565b611dc36125a3565b6001600160a01b038116611de95760405162461bcd60e51b815260040161081790613d56565b600154600160a81b900460ff1615611e395760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610817565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b179055604051600080516020614052833981519152916108da918490613769565b611e916125a3565b60118290558015611ea25780611ea4565b425b60125560408051838152602081018390527fddd57316dedc944a54a2d4ccbec17883e25f23eebb11ea1a4800f36502a469f09101610b40565b6001600160a01b0381166000908152600c60205260409020606090610d91908585612a77565b611f0b6125a3565b6001600160a01b038116611f315760405162461bcd60e51b815260040161081790613d56565b60058054610100600160a81b0319166101006001600160a01b038416021790556040517f9985022676a73860c32a3b91ea7a7dfe2d5e87c148f50eb519d8b0f33ab7f8b9906108da9083906130f5565b611f896125a3565b6001600160a01b038116611faf5760405162461bcd60e51b815260040161081790613d56565b600e80546001600160a01b0319166001600160a01b0383161790556040517f63bb8d8b29c197c8b58de8e513ab743bce7bf84c87adc05f2cc18e609d5d0e86906108da9083906130f5565b60055460ff16156120435760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b6044820152606401610817565b6005805460ff19166001908117909155600455565b60035460ff161561207b5760405162461bcd60e51b8152600401610817906137b9565b60016004600082825461208e919061382c565b90915550506004546001600160a01b03831660009081526008602052604090205433908490899060ff166120d45760405162461bcd60e51b81526004016108179061383f565b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156121195760405162461bcd60e51b8152600401610817906138d5565b6121238383612818565b61213f5760405162461bcd60e51b815260040161081790613903565b61214f8c8c8c8c8c8c8c8c612892565b50505060045481146121735760405162461bcd60e51b81526004016108179061389e565b505050505050505050565b6001600160a01b0383166000908152601360205260409020546060908190819081908511156121c3576001600160a01b03871660009081526013602052604090205494505b846001600160401b038111156121db576121db613dba565b604051908082528060200260200182016040528015612204578160200160208202803683370190505b509350846001600160401b0381111561221f5761221f613dba565b604051908082528060200260200182016040528015612248578160200160208202803683370190505b509150846001600160401b0381111561226357612263613dba565b60405190808252806020026020018201604052801561228c578160200160208202803683370190505b509250846001600160401b038111156122a7576122a7613dba565b6040519080825280602002602001820160405280156122d0578160200160208202803683370190505b50905060005b858110156123ea576001600160a01b0388166000908152601360205260408120612300838a61382c565b815481106123105761231061386f565b6000918252602090912001546001600160a01b03169050612331818a612db2565b8584815181106123435761234361386f565b6020026020010185858151811061235c5761235c61386f565b602002602001018281525082151515158152505050808683815181106123845761238461386f565b6001600160a01b039283166020918202929092018101919091528282166000908152600782526040808220938d16825292909152205485518690849081106123ce576123ce61386f565b6020908102919091010152506123e381613885565b90506122d6565b5093509350935093565b60035460ff16156124175760405162461bcd60e51b8152600401610817906137b9565b3361242a6101408301610120840161316f565b6001600160a01b03811660009081526008602052604090205460a08401359060ff166124685760405162461bcd60e51b81526004016108179061383f565b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156124ad5760405162461bcd60e51b8152600401610817906138d5565b6124b78383612818565b6124d35760405162461bcd60e51b815260040161081790613903565b60065460405163584ceab960e11b81526000916001600160a01b03169063b099d57290612504908890600401613e3e565b6020604051808303816000875af1158015612523573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125479190613cc2565b6000818152600b60205260409081902080546001600160a01b0319163390811790915590519192507ff226e2c0a5c137f1fc5331d8dabe76ea9810e121bc9b18737125bcaa884daa8091611190919060a0890135908590613cdb565b6000546001600160a01b031633146126155760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610817565b565b61266f83846001600160a01b031663a9059cbb858560405160240161263d929190613783565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612f9d565b505050565b6001600160a01b03821660009081526008602052604090205460ff166126ac5760405162461bcd60e51b81526004016108179061383f565b6001600160a01b038084166000908152600760209081526040808320938616808452939091528082205490516370a0823160e01b81529092906370a08231906126f99030906004016130f5565b602060405180830381865afa158015612716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273a9190613cc2565b905060008211801561274c5750818110155b15612765576127656001600160a01b0385168484612617565b6001600160a01b0380861660009081526007602090815260408083209388168352928152828220829055601390522061279e9086612be3565b156127c5576001600160a01b03841660009081526013602052604090206127c59086612c59565b604080516001600160a01b0380881682528087166020830152851691810191909152606081018390527f5aecd162bb2e2f8d70294fe483d6a8596cf8555a650f08a962220feaba5c21fe90608001611190565b6001600160a01b03808316600090815260106020908152604080832093851683529290529081205442108061288957506001600160a01b03808416600090815260106020908152604080832093861683529290522054158015612889575042601154601254612887919061382c565b115b90505b92915050565b3360009081526007602090815260408083206001600160a01b0386168452909152812080548892906128c5908490613d19565b909155506000905060ff821615612965576005546040516349d8615760e11b81526101009091046001600160a01b0316906393b0c2ae9061291b908c908c908c908c908c908c908c906000908d90600401613f5f565b6020604051808303816000875af115801561293a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061295e9190613cfc565b90506129ed565b6005546040516214e17b60e41b81526101009091046001600160a01b03169063014e17b0906129a7908c908c908c908c908c908c908c90600090600401613fbb565b6020604051808303816000875af11580156129c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129ea9190613cfc565b90505b6001600160a01b038116600090815260096020908152604080832080546001600160a01b031916339081179091558352600c9091529020612a2e9082612b91565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838188336000604051612a649493929190613d2c565b60405180910390a1505050505050505050565b60606000612a85838561382c565b8554909150811115612a95575083545b838111612ab2575050604080516000815260208101909152610d94565b6000612abe8583613d19565b90506000816001600160401b03811115612ada57612ada613dba565b604051908082528060200260200182016040528015612b03578160200160208202803683370190505b50905060005b82811015612b865787612b1c888361382c565b81548110612b2c57612b2c61386f565b9060005260206000200160009054906101000a90046001600160a01b0316828281518110612b5c57612b5c61386f565b6001600160a01b039092166020928302919091019091015280612b7e81613885565b915050612b09565b509695505050505050565b612b9b8282612be3565b6109c65781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555050565b81546000908103612bf65750600061288c565b6001600160a01b038216600090815260018401602052604090205480151580612c515750826001600160a01b031684600001600081548110612c3a57612c3a61386f565b6000918252602090912001546001600160a01b0316145b949350505050565b612c638282612be3565b612ca55760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b6044820152606401610817565b6001600160a01b0381166000908152600180840160205260408220548454909291612ccf91613d19565b9050808214612d5b576000846000018281548110612cef57612cef61386f565b60009182526020909120015485546001600160a01b0390911691508190869085908110612d1e57612d1e61386f565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b8354849080612d6c57612d6c61400c565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b6001600160a01b038116600090815260086020526040812054819060ff168015612e0057506001600160a01b0380851660009081526007602090815260408083209387168352929052205415155b15612e8e576001600160a01b038085166000908152601060209081526040808320938716835292905290812054612e4657601154601254612e41919061382c565b612e6d565b6001600160a01b038086166000908152601060209081526040808320938816835292905220545b9050428111925082612e80576000612e8a565b612e8a4282613d19565b9150505b9250929050565b610f6984856001600160a01b03166323b872dd86868660405160240161263d93929190613d7f565b6001600160a01b0383166000908152601360205260409020612edf9085612b91565b6001600160a01b03808516600090815260076020908152604080832093871683529290529081208054849290612f1690849061382c565b9091555050601154612f28904261382c565b6001600160a01b03858116600081815260106020908152604080832089861680855290835292819020959095558451928352820152918201849052821660608201527ff462a3a00cd477c2aa6252dfbff27a7cf55ad6f75c526f4650b5bb92ef2912689060800160405180910390a150505050565b6000612fb26001600160a01b03841683612ff7565b90508051600014158015612fd7575080806020019051810190612fd5919061379c565b155b1561266f5782604051635274afe760e01b815260040161081791906130f5565b60606128898383600084600080856001600160a01b0316848660405161301d9190614022565b60006040518083038185875af1925050503d806000811461305a576040519150601f19603f3d011682016040523d82523d6000602084013e61305f565b606091505b509150915061306f868383613079565b9695505050505050565b60608261308e57613089826130cc565b610d94565b81511580156130a557506001600160a01b0384163b155b156130c55783604051639996b31560e01b815260040161081791906130f5565b5080610d94565b8051156130dc5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0391909116815260200190565b6001600160a01b03811681146109a757600080fd5b803561312981613109565b919050565b60008060006060848603121561314357600080fd5b833561314e81613109565b9250602084013561315e81613109565b929592945050506040919091013590565b60006020828403121561318157600080fd5b8135610d9481613109565b80151581146109a757600080fd5b6000602082840312156131ac57600080fd5b8135610d948161318c565b600080604083850312156131ca57600080fd5b82356131d581613109565b946020939093013593505050565b600080604083850312156131f657600080fd5b823561320181613109565b915060208301356132118161318c565b809150509250929050565b60008083601f84011261322e57600080fd5b5081356001600160401b0381111561324557600080fd5b6020830191508360208260051b8501011115612e8e57600080fd5b6000806000806060858703121561327657600080fd5b84356001600160401b0381111561328c57600080fd5b6132988782880161321c565b90955093505060208501356132ac81613109565b915060408501356132bc81613109565b939692955090935050565b6000602082840312156132d957600080fd5b5035919050565b600080600080600080600060c0888a0312156132fb57600080fd5b87356001600160401b0381111561331157600080fd5b61331d8a828b0161321c565b909850965050602088013594506040880135935060608801359250608088013561334681613109565b915060a088013561335681613109565b8091505092959891949750929550565b60008060006060848603121561337b57600080fd5b8335925060208401359150604084013561339481613109565b809150509250925092565b600081518084526020808501945080840160005b838110156133d85781516001600160a01b0316875295820195908201906001016133b3565b509495945050505050565b602081526000612889602083018461339f565b60008060006040848603121561340b57600080fd5b83356001600160401b0381111561342157600080fd5b61342d8682870161321c565b909450925050602084013561339481613109565b60008060006060848603121561345657600080fd5b833561346181613109565b9250602084013561347181613109565b9150604084013561339481613109565b60006020828403121561349357600080fd5b81356001600160401b038111156134a957600080fd5b820160c08185031215610d9457600080fd5b600080600080608085870312156134d157600080fd5b8435935060208501356134e381613109565b92506040850135915060608501356132bc81613109565b60008060006060848603121561350f57600080fd5b833561351a81613109565b95602085013595506040909401359392505050565b6000806040838503121561354257600080fd5b823561354d81613109565b9150602083013561321181613109565b6000806000806060858703121561357357600080fd5b84356001600160401b0381111561358957600080fd5b6135958782880161321c565b90955093505060208501356135a981613109565b9396929550929360400135925050565b600080604083850312156135cc57600080fd5b50508035926020909101359150565b803560ff8116811461312957600080fd5b60008060008060008060008060e0898b03121561360857600080fd5b88356001600160401b0381111561361e57600080fd5b61362a8b828c0161321c565b909950975050602089013595506040890135945060608901359350608089013561365381613109565b925060a089013561366381613109565b915061367160c08a016135db565b90509295985092959890939650565b600081518084526020808501945080840160005b838110156133d857815187529582019590820190600101613694565b6080815260006136c3608083018761339f565b6020838203818501526136d68288613680565b8481036040860152865180825282880193509082019060005b8181101561370d5784511515835293830193918301916001016136ef565b505084810360608601526137218187613680565b9998505050505050505050565b60006020828403121561374057600080fd5b81356001600160401b0381111561375657600080fd5b82016101608185031215610d9457600080fd5b6001600160a01b0392831681529116602082015260400190565b6001600160a01b03929092168252602082015260400190565b6000602082840312156137ae57600080fd5b8151610d948161318c565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082018082111561288c5761288c613816565b602080825260169082015275155b9cdd5c1c1bdc9d19590818dbdb1b185d195c985b60521b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006001820161389757613897613816565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602080825260149082015273496e73756666696369656e742062616c616e636560601b604082015260600190565b60208082526010908201526f119c99594818995d08195e1c1a5c995960821b604082015260600190565b6000808335601e1984360301811261394457600080fd5b83016020810192503590506001600160401b0381111561396357600080fd5b8060051b3603821315612e8e57600080fd5b803561ffff8116811461312957600080fd5b8035600281900b811461312957600080fd5b803562ffffff8116811461312957600080fd5b81835260006001600160fb1b038311156139c557600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b858110156133d85761ffff613a0283613975565b16875260ff613a128484016135db565b16838801526040613a24818401613987565b60020b9088015260609687019691909101906001016139ee565b81835260006020808501808196508560051b810191508460005b87811015613ac55782840389528135601e19883603018112613a7957600080fd5b870185810190356001600160401b03811115613a9457600080fd5b606081023603821315613aa657600080fd5b613ab18682846139de565b9a87019a9550505090840190600101613a58565b5091979650505050505050565b6000823561015e19833603018112613ae957600080fd5b90910192915050565b81835260006020808501808196508560051b810191508460005b87811015613ac5578284038952613b238288613ad2565b61016081358652613b35878301613975565b61ffff16878701526040613b4a838201613975565b61ffff1690870152606082810135908701526080613b698184016135db565b60ff169087015260a0613b7d838201613987565b613b8b8289018260020b9052565b505060c0613b9a818401613999565b62ffffff169087015260e0613bb18382018461392d565b83838a0152613bc3848a0182846139ac565b9350505050610100613bd78184018461392d565b888403838a0152613be98482846139ac565b9350505050610120613bfc8184016135db565b60ff1690870152610140613c128382018461392d565b935087830382890152613c26838583613a3e565b9c89019c97505050928601925050600101613b0c565b602081526000613c4c838461392d565b60c06020850152613c6160e085018284613af2565b9150506020840135604084015260408401356060840152606084013560808401526080840135613c9081613109565b6001600160a01b0390811660a08581019190915285013590613cb182613109565b1660c0939093019290925250919050565b600060208284031215613cd457600080fd5b5051919050565b6001600160a01b039390931683526020830191909152604082015260600190565b600060208284031215613d0e57600080fd5b8151610d9481613109565b8181038181111561288c5761288c613816565b6001600160a01b039485168152602081019390935292166040820152901515606082015260800190565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b808202811582820484141761288c5761288c613816565b634e487b7160e01b600052604160045260246000fd5b6000808335601e19843603018112613de757600080fd5b83016020810192503590506001600160401b03811115613e0657600080fd5b803603821315612e8e57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000613e4e8384613dd0565b610160806020860152613e6661018086018385613e15565b9250613e7460208701613975565b61ffff811660408701529150613e8c60408701613975565b61ffff811660608701529150613ea460608701613987565b9150613eb5608086018360020b9052565b613ec1608087016135db565b60ff811660a0870152915060a086013560c086015260c086013560e0860152610100915060e086013582860152613ef982870161311e565b9150610120613f12818701846001600160a01b03169052565b613f1d81880161311e565b925050610140613f37818701846001600160a01b03169052565b613f42818801613999565b925050613f558186018362ffffff169052565b5090949350505050565b6000610100808352613f748184018c8e613af2565b602084019a909a525050604081019690965260608601949094526001600160a01b039283166080860152911660a0840152151560c083015260ff1660e09091015292915050565b60e081526000613fcf60e083018a8c613af2565b602083019890985250604081019590955260608501939093526001600160a01b0391821660808501521660a0830152151560c09091015292915050565b634e487b7160e01b600052603160045260246000fd5b6000825160005b818110156140435760208186018101518583015201614029565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca2646970667358221220d3eee2ef31dc18cebb8222e950937a6269b160a692c0c01777d4b369a565f74464736f6c63430008140033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102c15760003560e01c80639168fdd11161017d578063cab7bf56116100d9578063ebc7977211610092578063ebc79772146106ef578063edc5de8e146106f7578063efac480d14610700578063f38329d914610713578063fa550eba14610736578063fd73489814610759578063fecc198b1461076c57600080fd5b8063cab7bf561461064d578063dd1d977314610678578063dd37861d1461068b578063e74e33e01461069e578063e81e52ee146106c9578063e94cdf5f146106dc57600080fd5b8063aba84bb111610136578063aba84bb1146105b6578063b1b5823b146105e0578063be899c89146105e9578063bea4ae20146105fc578063c0c53b8b1461060f578063c3b83f5f14610622578063c99252881461063557600080fd5b80639168fdd11461053857806391b4ded914610561578063924fbde11461056a57806394bd23131461057d578063a12d5b0714610590578063a13d6b6a146105a357600080fd5b806342d851ed1161022c57806353a47bb7116101e557806353a47bb71461049e5780635c975abb146104b157806379ba5097146104ce5780637eda2d7b146104d65780638da5cb5b146104e95780638f974cd9146104fc57806390e3afb21461052557600080fd5b806342d851ed1461040957806344cab7ee14610432578063485b23bf146104455780634d1ac474146104655780634fa98ca51461047857806352aa17a21461048b57600080fd5b806316c38b3c1161027e57806316c38b3c14610381578063188aa45d146103945780631a10bd76146103a7578063275fd247146103ba5780633a572121146103cd5780633a687e2b146103f657600080fd5b806304de96c5146102c6578063084a2fa4146102ef57806311e9c08d1461030457806313af4035146103325780631590a4a4146103455780631627540c1461036e575b600080fd5b6006546102d9906001600160a01b031681565b6040516102e691906130f5565b60405180910390f35b6103026102fd36600461312e565b610795565b005b61032461031236600461316f565b600a6020526000908152604090205481565b6040519081526020016102e6565b61030261034036600461316f565b6107c9565b61032461035336600461316f565b6001600160a01b03166000908152600c602052604090205490565b61030261037c36600461316f565b6108e5565b61030261038f36600461319a565b610938565b6103026103a23660046131b7565b6109aa565b6103026103b53660046131e3565b6109ca565b6103026103c8366004613260565b610b4c565b6102d96103db3660046132c7565b600b602052600090815260409020546001600160a01b031681565b6103026104043660046132e0565b610c45565b6102d96104173660046132c7565b600f602052600090815260409020546001600160a01b031681565b600e546102d9906001600160a01b031681565b610458610453366004613366565b610d6b565b6040516102e691906133e3565b6103026104733660046133f6565b610d9b565b610302610486366004613441565b610f6f565b610302610499366004613481565b610fe1565b6001546102d9906001600160a01b031681565b6003546104be9060ff1681565b60405190151581526020016102e6565b61030261119f565b6103026104e43660046134bb565b611277565b6000546102d9906001600160a01b031681565b6102d961050a36600461316f565b6009602052600090815260409020546001600160a01b031681565b6103026105333660046133f6565b61155b565b61032461054636600461316f565b6001600160a01b03166000908152600d602052604090205490565b61032460025481565b6104586105783660046134fa565b6115c6565b61030261058b36600461316f565b6115ec565b61030261059e3660046134bb565b611665565b6103026105b136600461316f565b611768565b6105c96105c436600461352f565b611acc565b6040805192151583526020830191909152016102e6565b61032460125481565b6103026105f736600461312e565b611ae4565b61030261060a36600461355d565b611b7b565b61030261061d366004613441565b611c65565b61030261063036600461316f565b611dbb565b6005546102d99061010090046001600160a01b031681565b61032461065b36600461352f565b600760209081526000928352604080842090915290825290205481565b6103026106863660046135b9565b611e89565b610458610699366004613366565b611edd565b6103246106ac36600461352f565b601060209081526000928352604080842090915290825290205481565b6103026106d736600461316f565b611f03565b6103026106ea36600461316f565b611f81565b610302611ffa565b61032460115481565b61030261070e3660046135ec565b612058565b6107266107213660046134fa565b61217e565b6040516102e694939291906136b0565b6104be61074436600461316f565b60086020526000908152604090205460ff1681565b61030261076736600461372e565b6123f4565b61032461077a36600461316f565b6001600160a01b031660009081526013602052604090205490565b61079d6125a3565b6001600160a01b0392831660009081526010602090815260408083209490951682529290925291902055565b6001600160a01b0381166108205760405162461bcd60e51b815260206004820152601960248201527804f776e657220616464726573732063616e6e6f74206265203603c1b60448201526064015b60405180910390fd5b600154600160a01b900460ff161561088c5760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b6064820152608401610817565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b0319909116178155604051600080516020614052833981519152916108da918490613769565b60405180910390a150565b6108ed6125a3565b600180546001600160a01b0319166001600160a01b0383161790556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce22906108da9083906130f5565b6109406125a3565b60035460ff161515811515146109a7576003805460ff191682151590811790915560ff161561096e57426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec5906020016108da565b50565b6109b26125a3565b6109c66001600160a01b0383163383612617565b5050565b6109d26125a3565b6001600160a01b0382166000908152600860205260409020805460ff19168215801591909117909155610a845760055460405163095ea7b360e01b81526001600160a01b038085169263095ea7b392610a3b926101009092049091169060001990600401613783565b6020604051808303816000875af1158015610a5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a7e919061379c565b50610b04565b60055460405163095ea7b360e01b81526001600160a01b038085169263095ea7b392610abf9261010090920490911690600090600401613783565b6020604051808303816000875af1158015610ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b02919061379c565b505b604080516001600160a01b038416815282151560208201527fb44b294feae8400b9db26080aaedb6cd054001acc015182a0d4216d9e0e7d49091015b60405180910390a15050565b60035460ff1615610b6f5760405162461bcd60e51b8152600401610817906137b9565b600160046000828254610b82919061382c565b9091555050600454610b926125a3565b6001600160a01b03831660009081526008602052604090205460ff16610bca5760405162461bcd60e51b81526004016108179061383f565b60005b84811015610c1c576000868683818110610be957610be961386f565b9050602002016020810190610bfe919061316f565b9050610c0b818686612674565b50610c1581613885565b9050610bcd565b506004548114610c3e5760405162461bcd60e51b81526004016108179061389e565b5050505050565b60035460ff1615610c685760405162461bcd60e51b8152600401610817906137b9565b600160046000828254610c7b919061382c565b90915550506004546001600160a01b03821660009081526008602052604090205433908390889060ff16610cc15760405162461bcd60e51b81526004016108179061383f565b6001600160a01b03808416600090815260076020908152604080832093861683529290522054811115610d065760405162461bcd60e51b8152600401610817906138d5565b610d108383612818565b610d2c5760405162461bcd60e51b815260040161081790613903565b610d3d8b8b8b8b8b8b8b6000612892565b5050506004548114610d615760405162461bcd60e51b81526004016108179061389e565b5050505050505050565b6001600160a01b0381166000908152600d60205260409020606090610d91908585612a77565b90505b9392505050565b60035460ff1615610dbe5760405162461bcd60e51b8152600401610817906137b9565b600160046000828254610dd1919061382c565b909155505060045460005b83811015610f47576000858583818110610df857610df861386f565b9050602002016020810190610e0d919061316f565b6001600160a01b0380821660009081526007602090815260408083209389168352929052205490915015610f36576001600160a01b0380821660009081526010602090815260408083209388168352929052205415801590610e9357506001600160a01b0380821660009081526010602090815260408083209388168352929052205442115b80610eda57506001600160a01b03808216600090815260106020908152604080832093881683529290522054158015610eda575042601154601254610ed8919061382c565b105b610f1d5760405162461bcd60e51b8152602060048201526014602482015273119c99594818995d081b9bdd08195e1c1a5c995960621b6044820152606401610817565b600054610f3690829086906001600160a01b0316612674565b50610f4081613885565b9050610ddc565b506004548114610f695760405162461bcd60e51b81526004016108179061389e565b50505050565b60035460ff1615610f925760405162461bcd60e51b8152600401610817906137b9565b600160046000828254610fa5919061382c565b9091555050600454610fb56125a3565b610fc0848484612674565b6004548114610f695760405162461bcd60e51b81526004016108179061389e565b60035460ff16156110045760405162461bcd60e51b8152600401610817906137b9565b3361101560c0830160a0840161316f565b6001600160a01b038116600090815260086020908152604090912054908401359060ff166110555760405162461bcd60e51b81526004016108179061383f565b6001600160a01b0380841660009081526007602090815260408083209386168352929052205481111561109a5760405162461bcd60e51b8152600401610817906138d5565b6110a48383612818565b6110c05760405162461bcd60e51b815260040161081790613903565b600e546040516306fea14960e21b81526000916001600160a01b031690631bfa8524906110f1908890600401613c3c565b6020604051808303816000875af1158015611110573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111349190613cc2565b6000818152600f602090815260409182902080546001600160a01b0319163390811790915591519293507f2a107e8d4cba0aa559543eb234520741dac54e7db15d8a96afc8df0dcb8f7525926111909291890135908590613cdb565b60405180910390a15050505050565b6001546001600160a01b031633146112175760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b6064820152608401610817565b60005460015460405160008051602061405283398151915292611248926001600160a01b0391821692911690613769565b60405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b60035460ff161561129a5760405162461bcd60e51b8152600401610817906137b9565b6001600460008282546112ad919061382c565b9091555050600454600e546001600160a01b0316331461131e5760405162461bcd60e51b815260206004820152602660248201527f4f6e6c792063616c6c61626c652066726f6d2053475054726164696e6750726f60448201526531b2b9b9b7b960d11b6064820152608401610817565b6000858152600f60205260409020546001600160a01b0316806113785760405162461bcd60e51b8152602060048201526012602482015271155b9adb9bdddb8814d1d4081d1a58dad95d60721b6044820152606401610817565b6001600160a01b03831661140057600560019054906101000a90046001600160a01b03166001600160a01b031663aeb0f1646040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113fd9190613cfc565b92505b6001600160a01b03831660009081526008602052604090205460ff166114385760405162461bcd60e51b81526004016108179061383f565b6001600160a01b0380821660009081526007602090815260408083209387168352929052205484111561147d5760405162461bcd60e51b8152600401610817906138d5565b6001600160a01b038082166000908152600760209081526040808320938716835292905290812080548692906114b4908490613d19565b90915550506001600160a01b03858116600090815260096020908152604080832080546001600160a01b0319169486169485179055928252600c9052206114fb9086612b91565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b640308385858360016040516115319493929190613d2c565b60405180910390a1506004548114610c3e5760405162461bcd60e51b81526004016108179061389e565b6115636125a3565b60005b82811015610f69576115b68484838181106115835761158361386f565b9050602002016020810190611598919061316f565b6001600160a01b038416600090815260136020526040902090612b91565b6115bf81613885565b9050611566565b6001600160a01b0383166000908152601360205260409020606090610d91908484612a77565b6115f46125a3565b6001600160a01b03811661161a5760405162461bcd60e51b815260040161081790613d56565b600680546001600160a01b0319166001600160a01b0383161790556040517f8b5670b3bc5c520b7a3580c753ba88065a561ad52a99674196859b044ea20285906108da9083906130f5565b60035460ff16156116885760405162461bcd60e51b8152600401610817906137b9565b60016004600082825461169b919061382c565b90915550506004546006546001600160a01b0316331461170d5760405162461bcd60e51b815260206004820152602760248201527f4f6e6c792063616c6c61626c652066726f6d204c69766554726164696e67507260448201526637b1b2b9b9b7b960c91b6064820152608401610817565b6000858152600b60205260409020546001600160a01b0316806113785760405162461bcd60e51b8152602060048201526013602482015272155b9adb9bdddb881b1a5d99481d1a58dad95d606a1b6044820152606401610817565b60055461010090046001600160a01b031633146117c75760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c7920616c6c6f7765642066726f6d2053706f727473414d4d00000000006044820152606401610817565b6001600160a01b0380821660009081526009602052604090205416806118205760405162461bcd60e51b815260206004820152600e60248201526d155b9adb9bdddb881d1a58dad95d60921b6044820152606401610817565b6001600160a01b0381166000908152600c602052604090206118429083612be3565b6118865760405162461bcd60e51b8152602060048201526015602482015274155b9adb9bdddb881858dd1a5d99481d1a58dad95d605a1b6044820152606401610817565b6000826001600160a01b031663242a8a6b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ea9190613cc2565b905060008115611a4d576000846001600160a01b031663d8dfeb456040518163ffffffff1660e01b8152600401602060405180830381865afa158015611934573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119589190613cfc565b90506000856001600160a01b031663d165dac26040518163ffffffff1660e01b8152600401602060405180830381865afa15801561199a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119be9190613cc2565b905080841115611a0d576000546119e2906001600160a01b03848116911683612617565b6119ec8185613d19565b92508215611a0857611a086001600160a01b0383168685612617565b611a4a565b6001600160a01b03808616600090815260076020908152604080832093861683529290529081208054869290611a4490849061382c565b90915550505b50505b7f66c0cec01a971dcd7f2bb22f7cc4b244c8f9ee6deb5540b073d0f10b4539e8b5848483604051611a8093929190613d7f565b60405180910390a16001600160a01b0383166000908152600c60205260409020611aaa9085612c59565b6001600160a01b0383166000908152600d60205260409020610f699085612b91565b600080611ad98484612db2565b909590945092505050565b60035460ff1615611b075760405162461bcd60e51b8152600401610817906137b9565b600160046000828254611b1a919061382c565b90915550506004546001600160a01b03831660009081526008602052604090205460ff16611b5a5760405162461bcd60e51b81526004016108179061383f565b611b6f6001600160a01b038416333085612e95565b610fc084848433612ebd565b60035460ff1615611b9e5760405162461bcd60e51b8152600401610817906137b9565b600160046000828254611bb1919061382c565b90915550506004546001600160a01b03831660009081526008602052604090205460ff16611bf15760405162461bcd60e51b81526004016108179061383f565b611c123330611c008786613da3565b6001600160a01b038716929190612e95565b60005b84811015610c1c576000868683818110611c3157611c3161386f565b9050602002016020810190611c46919061316f565b9050611c5481868633612ebd565b50611c5e81613885565b9050611c15565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b0316600081158015611caa5750825b90506000826001600160401b03166001148015611cc65750303b155b905081158015611cd4575080155b15611cf25760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611d1c57845460ff60401b1916600160401b1785555b611d25886107c9565b611d2d611ffa565b60058054610100600160a81b0319166101006001600160a01b038a81169190910291909117909155600680546001600160a01b0319169188169190911790558315610d6157845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050505050565b611dc36125a3565b6001600160a01b038116611de95760405162461bcd60e51b815260040161081790613d56565b600154600160a81b900460ff1615611e395760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b6044820152606401610817565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b179055604051600080516020614052833981519152916108da918490613769565b611e916125a3565b60118290558015611ea25780611ea4565b425b60125560408051838152602081018390527fddd57316dedc944a54a2d4ccbec17883e25f23eebb11ea1a4800f36502a469f09101610b40565b6001600160a01b0381166000908152600c60205260409020606090610d91908585612a77565b611f0b6125a3565b6001600160a01b038116611f315760405162461bcd60e51b815260040161081790613d56565b60058054610100600160a81b0319166101006001600160a01b038416021790556040517f9985022676a73860c32a3b91ea7a7dfe2d5e87c148f50eb519d8b0f33ab7f8b9906108da9083906130f5565b611f896125a3565b6001600160a01b038116611faf5760405162461bcd60e51b815260040161081790613d56565b600e80546001600160a01b0319166001600160a01b0383161790556040517f63bb8d8b29c197c8b58de8e513ab743bce7bf84c87adc05f2cc18e609d5d0e86906108da9083906130f5565b60055460ff16156120435760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b6044820152606401610817565b6005805460ff19166001908117909155600455565b60035460ff161561207b5760405162461bcd60e51b8152600401610817906137b9565b60016004600082825461208e919061382c565b90915550506004546001600160a01b03831660009081526008602052604090205433908490899060ff166120d45760405162461bcd60e51b81526004016108179061383f565b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156121195760405162461bcd60e51b8152600401610817906138d5565b6121238383612818565b61213f5760405162461bcd60e51b815260040161081790613903565b61214f8c8c8c8c8c8c8c8c612892565b50505060045481146121735760405162461bcd60e51b81526004016108179061389e565b505050505050505050565b6001600160a01b0383166000908152601360205260409020546060908190819081908511156121c3576001600160a01b03871660009081526013602052604090205494505b846001600160401b038111156121db576121db613dba565b604051908082528060200260200182016040528015612204578160200160208202803683370190505b509350846001600160401b0381111561221f5761221f613dba565b604051908082528060200260200182016040528015612248578160200160208202803683370190505b509150846001600160401b0381111561226357612263613dba565b60405190808252806020026020018201604052801561228c578160200160208202803683370190505b509250846001600160401b038111156122a7576122a7613dba565b6040519080825280602002602001820160405280156122d0578160200160208202803683370190505b50905060005b858110156123ea576001600160a01b0388166000908152601360205260408120612300838a61382c565b815481106123105761231061386f565b6000918252602090912001546001600160a01b03169050612331818a612db2565b8584815181106123435761234361386f565b6020026020010185858151811061235c5761235c61386f565b602002602001018281525082151515158152505050808683815181106123845761238461386f565b6001600160a01b039283166020918202929092018101919091528282166000908152600782526040808220938d16825292909152205485518690849081106123ce576123ce61386f565b6020908102919091010152506123e381613885565b90506122d6565b5093509350935093565b60035460ff16156124175760405162461bcd60e51b8152600401610817906137b9565b3361242a6101408301610120840161316f565b6001600160a01b03811660009081526008602052604090205460a08401359060ff166124685760405162461bcd60e51b81526004016108179061383f565b6001600160a01b038084166000908152600760209081526040808320938616835292905220548111156124ad5760405162461bcd60e51b8152600401610817906138d5565b6124b78383612818565b6124d35760405162461bcd60e51b815260040161081790613903565b60065460405163584ceab960e11b81526000916001600160a01b03169063b099d57290612504908890600401613e3e565b6020604051808303816000875af1158015612523573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125479190613cc2565b6000818152600b60205260409081902080546001600160a01b0319163390811790915590519192507ff226e2c0a5c137f1fc5331d8dabe76ea9810e121bc9b18737125bcaa884daa8091611190919060a0890135908590613cdb565b6000546001600160a01b031633146126155760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b6064820152608401610817565b565b61266f83846001600160a01b031663a9059cbb858560405160240161263d929190613783565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612f9d565b505050565b6001600160a01b03821660009081526008602052604090205460ff166126ac5760405162461bcd60e51b81526004016108179061383f565b6001600160a01b038084166000908152600760209081526040808320938616808452939091528082205490516370a0823160e01b81529092906370a08231906126f99030906004016130f5565b602060405180830381865afa158015612716573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273a9190613cc2565b905060008211801561274c5750818110155b15612765576127656001600160a01b0385168484612617565b6001600160a01b0380861660009081526007602090815260408083209388168352928152828220829055601390522061279e9086612be3565b156127c5576001600160a01b03841660009081526013602052604090206127c59086612c59565b604080516001600160a01b0380881682528087166020830152851691810191909152606081018390527f5aecd162bb2e2f8d70294fe483d6a8596cf8555a650f08a962220feaba5c21fe90608001611190565b6001600160a01b03808316600090815260106020908152604080832093851683529290529081205442108061288957506001600160a01b03808416600090815260106020908152604080832093861683529290522054158015612889575042601154601254612887919061382c565b115b90505b92915050565b3360009081526007602090815260408083206001600160a01b0386168452909152812080548892906128c5908490613d19565b909155506000905060ff821615612965576005546040516349d8615760e11b81526101009091046001600160a01b0316906393b0c2ae9061291b908c908c908c908c908c908c908c906000908d90600401613f5f565b6020604051808303816000875af115801561293a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061295e9190613cfc565b90506129ed565b6005546040516214e17b60e41b81526101009091046001600160a01b03169063014e17b0906129a7908c908c908c908c908c908c908c90600090600401613fbb565b6020604051808303816000875af11580156129c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129ea9190613cfc565b90505b6001600160a01b038116600090815260096020908152604080832080546001600160a01b031916339081179091558352600c9091529020612a2e9082612b91565b7f7ae5c9299a3bae0b94af6ac3b4a893bc2c44adfbd65479c7882294b4b64030838188336000604051612a649493929190613d2c565b60405180910390a1505050505050505050565b60606000612a85838561382c565b8554909150811115612a95575083545b838111612ab2575050604080516000815260208101909152610d94565b6000612abe8583613d19565b90506000816001600160401b03811115612ada57612ada613dba565b604051908082528060200260200182016040528015612b03578160200160208202803683370190505b50905060005b82811015612b865787612b1c888361382c565b81548110612b2c57612b2c61386f565b9060005260206000200160009054906101000a90046001600160a01b0316828281518110612b5c57612b5c61386f565b6001600160a01b039092166020928302919091019091015280612b7e81613885565b915050612b09565b509695505050505050565b612b9b8282612be3565b6109c65781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555050565b81546000908103612bf65750600061288c565b6001600160a01b038216600090815260018401602052604090205480151580612c515750826001600160a01b031684600001600081548110612c3a57612c3a61386f565b6000918252602090912001546001600160a01b0316145b949350505050565b612c638282612be3565b612ca55760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b6044820152606401610817565b6001600160a01b0381166000908152600180840160205260408220548454909291612ccf91613d19565b9050808214612d5b576000846000018281548110612cef57612cef61386f565b60009182526020909120015485546001600160a01b0390911691508190869085908110612d1e57612d1e61386f565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b8354849080612d6c57612d6c61400c565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b6001600160a01b038116600090815260086020526040812054819060ff168015612e0057506001600160a01b0380851660009081526007602090815260408083209387168352929052205415155b15612e8e576001600160a01b038085166000908152601060209081526040808320938716835292905290812054612e4657601154601254612e41919061382c565b612e6d565b6001600160a01b038086166000908152601060209081526040808320938816835292905220545b9050428111925082612e80576000612e8a565b612e8a4282613d19565b9150505b9250929050565b610f6984856001600160a01b03166323b872dd86868660405160240161263d93929190613d7f565b6001600160a01b0383166000908152601360205260409020612edf9085612b91565b6001600160a01b03808516600090815260076020908152604080832093871683529290529081208054849290612f1690849061382c565b9091555050601154612f28904261382c565b6001600160a01b03858116600081815260106020908152604080832089861680855290835292819020959095558451928352820152918201849052821660608201527ff462a3a00cd477c2aa6252dfbff27a7cf55ad6f75c526f4650b5bb92ef2912689060800160405180910390a150505050565b6000612fb26001600160a01b03841683612ff7565b90508051600014158015612fd7575080806020019051810190612fd5919061379c565b155b1561266f5782604051635274afe760e01b815260040161081791906130f5565b60606128898383600084600080856001600160a01b0316848660405161301d9190614022565b60006040518083038185875af1925050503d806000811461305a576040519150601f19603f3d011682016040523d82523d6000602084013e61305f565b606091505b509150915061306f868383613079565b9695505050505050565b60608261308e57613089826130cc565b610d94565b81511580156130a557506001600160a01b0384163b155b156130c55783604051639996b31560e01b815260040161081791906130f5565b5080610d94565b8051156130dc5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0391909116815260200190565b6001600160a01b03811681146109a757600080fd5b803561312981613109565b919050565b60008060006060848603121561314357600080fd5b833561314e81613109565b9250602084013561315e81613109565b929592945050506040919091013590565b60006020828403121561318157600080fd5b8135610d9481613109565b80151581146109a757600080fd5b6000602082840312156131ac57600080fd5b8135610d948161318c565b600080604083850312156131ca57600080fd5b82356131d581613109565b946020939093013593505050565b600080604083850312156131f657600080fd5b823561320181613109565b915060208301356132118161318c565b809150509250929050565b60008083601f84011261322e57600080fd5b5081356001600160401b0381111561324557600080fd5b6020830191508360208260051b8501011115612e8e57600080fd5b6000806000806060858703121561327657600080fd5b84356001600160401b0381111561328c57600080fd5b6132988782880161321c565b90955093505060208501356132ac81613109565b915060408501356132bc81613109565b939692955090935050565b6000602082840312156132d957600080fd5b5035919050565b600080600080600080600060c0888a0312156132fb57600080fd5b87356001600160401b0381111561331157600080fd5b61331d8a828b0161321c565b909850965050602088013594506040880135935060608801359250608088013561334681613109565b915060a088013561335681613109565b8091505092959891949750929550565b60008060006060848603121561337b57600080fd5b8335925060208401359150604084013561339481613109565b809150509250925092565b600081518084526020808501945080840160005b838110156133d85781516001600160a01b0316875295820195908201906001016133b3565b509495945050505050565b602081526000612889602083018461339f565b60008060006040848603121561340b57600080fd5b83356001600160401b0381111561342157600080fd5b61342d8682870161321c565b909450925050602084013561339481613109565b60008060006060848603121561345657600080fd5b833561346181613109565b9250602084013561347181613109565b9150604084013561339481613109565b60006020828403121561349357600080fd5b81356001600160401b038111156134a957600080fd5b820160c08185031215610d9457600080fd5b600080600080608085870312156134d157600080fd5b8435935060208501356134e381613109565b92506040850135915060608501356132bc81613109565b60008060006060848603121561350f57600080fd5b833561351a81613109565b95602085013595506040909401359392505050565b6000806040838503121561354257600080fd5b823561354d81613109565b9150602083013561321181613109565b6000806000806060858703121561357357600080fd5b84356001600160401b0381111561358957600080fd5b6135958782880161321c565b90955093505060208501356135a981613109565b9396929550929360400135925050565b600080604083850312156135cc57600080fd5b50508035926020909101359150565b803560ff8116811461312957600080fd5b60008060008060008060008060e0898b03121561360857600080fd5b88356001600160401b0381111561361e57600080fd5b61362a8b828c0161321c565b909950975050602089013595506040890135945060608901359350608089013561365381613109565b925060a089013561366381613109565b915061367160c08a016135db565b90509295985092959890939650565b600081518084526020808501945080840160005b838110156133d857815187529582019590820190600101613694565b6080815260006136c3608083018761339f565b6020838203818501526136d68288613680565b8481036040860152865180825282880193509082019060005b8181101561370d5784511515835293830193918301916001016136ef565b505084810360608601526137218187613680565b9998505050505050505050565b60006020828403121561374057600080fd5b81356001600160401b0381111561375657600080fd5b82016101608185031215610d9457600080fd5b6001600160a01b0392831681529116602082015260400190565b6001600160a01b03929092168252602082015260400190565b6000602082840312156137ae57600080fd5b8151610d948161318c565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082018082111561288c5761288c613816565b602080825260169082015275155b9cdd5c1c1bdc9d19590818dbdb1b185d195c985b60521b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b60006001820161389757613897613816565b5060010190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602080825260149082015273496e73756666696369656e742062616c616e636560601b604082015260600190565b60208082526010908201526f119c99594818995d08195e1c1a5c995960821b604082015260600190565b6000808335601e1984360301811261394457600080fd5b83016020810192503590506001600160401b0381111561396357600080fd5b8060051b3603821315612e8e57600080fd5b803561ffff8116811461312957600080fd5b8035600281900b811461312957600080fd5b803562ffffff8116811461312957600080fd5b81835260006001600160fb1b038311156139c557600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b858110156133d85761ffff613a0283613975565b16875260ff613a128484016135db565b16838801526040613a24818401613987565b60020b9088015260609687019691909101906001016139ee565b81835260006020808501808196508560051b810191508460005b87811015613ac55782840389528135601e19883603018112613a7957600080fd5b870185810190356001600160401b03811115613a9457600080fd5b606081023603821315613aa657600080fd5b613ab18682846139de565b9a87019a9550505090840190600101613a58565b5091979650505050505050565b6000823561015e19833603018112613ae957600080fd5b90910192915050565b81835260006020808501808196508560051b810191508460005b87811015613ac5578284038952613b238288613ad2565b61016081358652613b35878301613975565b61ffff16878701526040613b4a838201613975565b61ffff1690870152606082810135908701526080613b698184016135db565b60ff169087015260a0613b7d838201613987565b613b8b8289018260020b9052565b505060c0613b9a818401613999565b62ffffff169087015260e0613bb18382018461392d565b83838a0152613bc3848a0182846139ac565b9350505050610100613bd78184018461392d565b888403838a0152613be98482846139ac565b9350505050610120613bfc8184016135db565b60ff1690870152610140613c128382018461392d565b935087830382890152613c26838583613a3e565b9c89019c97505050928601925050600101613b0c565b602081526000613c4c838461392d565b60c06020850152613c6160e085018284613af2565b9150506020840135604084015260408401356060840152606084013560808401526080840135613c9081613109565b6001600160a01b0390811660a08581019190915285013590613cb182613109565b1660c0939093019290925250919050565b600060208284031215613cd457600080fd5b5051919050565b6001600160a01b039390931683526020830191909152604082015260600190565b600060208284031215613d0e57600080fd5b8151610d9481613109565b8181038181111561288c5761288c613816565b6001600160a01b039485168152602081019390935292166040820152901515606082015260800190565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b808202811582820484141761288c5761288c613816565b634e487b7160e01b600052604160045260246000fd5b6000808335601e19843603018112613de757600080fd5b83016020810192503590506001600160401b03811115613e0657600080fd5b803603821315612e8e57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000613e4e8384613dd0565b610160806020860152613e6661018086018385613e15565b9250613e7460208701613975565b61ffff811660408701529150613e8c60408701613975565b61ffff811660608701529150613ea460608701613987565b9150613eb5608086018360020b9052565b613ec1608087016135db565b60ff811660a0870152915060a086013560c086015260c086013560e0860152610100915060e086013582860152613ef982870161311e565b9150610120613f12818701846001600160a01b03169052565b613f1d81880161311e565b925050610140613f37818701846001600160a01b03169052565b613f42818801613999565b925050613f558186018362ffffff169052565b5090949350505050565b6000610100808352613f748184018c8e613af2565b602084019a909a525050604081019690965260608601949094526001600160a01b039283166080860152911660a0840152151560c083015260ff1660e09091015292915050565b60e081526000613fcf60e083018a8c613af2565b602083019890985250604081019590955260608501939093526001600160a01b0391821660808501521660a0830152151560c09091015292915050565b634e487b7160e01b600052603160045260246000fd5b6000825160005b818110156140435760208186018101518583015201614029565b50600092019182525091905056feb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159ca2646970667358221220d3eee2ef31dc18cebb8222e950937a6269b160a692c0c01777d4b369a565f74464736f6c63430008140033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.