Source Code
Overview
ETH Balance
0 ETH
More Info
ContractCreator
Multichain Info
N/A
Loading...
Loading
Contract Name:
StakingThalesBettingProxy
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@thales-dao/contracts/contracts/interfaces/IStakingThales.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 "./../AMM/Ticket.sol";
contract StakingThalesBettingProxy 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;
IStakingThales public stakingThales;
IERC20 public stakingCollateral;
mapping(address => address) public ticketToUser;
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;
/* ========== CONSTRUCTOR ========== */
function initialize(
address _owner,
address _sportsAMMV2,
address _liveTradingProcessor,
address _stakingThales,
address _stakingToken
) external initializer {
setOwner(_owner);
initNonReentrant();
sportsAMM = ISportsAMMV2(_sportsAMMV2);
liveTradingProcessor = ILiveTradingProcessor(_liveTradingProcessor);
stakingThales = IStakingThales(_stakingThales);
stakingCollateral = IERC20(_stakingToken);
stakingCollateral.approve(_stakingThales, MAX_APPROVAL);
stakingCollateral.approve(_sportsAMMV2, MAX_APPROVAL);
stakingCollateral.approve(_liveTradingProcessor, MAX_APPROVAL);
}
/// @notice buy a system bet ticket for a user if he has enough staked tokens
function tradeSystemBet(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
uint8 _systemBetDenominator
) external notPaused nonReentrant {
_trade(_tradeData, _buyInAmount, _expectedQuote, _additionalSlippage, _referrer, _systemBetDenominator);
}
/// @notice buy a ticket for a user if he has enough staked tokens
function trade(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer
) external notPaused nonReentrant {
_trade(_tradeData, _buyInAmount, _expectedQuote, _additionalSlippage, _referrer, 0);
}
function _trade(
ISportsAMMV2.TradeData[] calldata _tradeData,
uint _buyInAmount,
uint _expectedQuote,
uint _additionalSlippage,
address _referrer,
uint8 _systemBetDenominator
) internal {
// signal decrease of stakingAmount
stakingThales.decreaseAndTransferStakedThales(msg.sender, _buyInAmount);
address _createdTicket;
if (_systemBetDenominator > 0) {
_createdTicket = sportsAMM.tradeSystemBet(
_tradeData,
_buyInAmount,
_expectedQuote,
_additionalSlippage,
_referrer,
address(stakingCollateral),
false,
_systemBetDenominator
);
} else {
_createdTicket = sportsAMM.trade(
_tradeData,
_buyInAmount,
_expectedQuote,
_additionalSlippage,
_referrer,
address(stakingCollateral),
false
);
}
ticketToUser[_createdTicket] = msg.sender;
activeTicketsPerUser[msg.sender].add(_createdTicket);
emit StakingTokensTrade(_createdTicket, _buyInAmount, msg.sender, false);
}
/// @notice request a live ticket for a user if he has enough staked tokens
function tradeLive(ILiveTradingProcessor.LiveTradeData calldata _liveTradeData) external notPaused {
require(_liveTradeData._collateral == address(stakingCollateral), "Use Staking collateral for live trade");
bytes32 _requestId = liveTradingProcessor.requestLiveTrade(_liveTradeData);
liveRequestsPerUser[_requestId] = msg.sender;
emit StakingTokensLiveTradeRequested(msg.sender, _liveTradeData._buyInAmount, _requestId);
}
/// @notice pre-confirm a live ticket purchase by transfering funds from the staking contract to this contract
function preConfirmLiveTrade(bytes32 requestId, uint _buyInAmount) external notPaused nonReentrant {
require(msg.sender == address(liveTradingProcessor), "Only callable from LiveTradingProcessor");
address _user = liveRequestsPerUser[requestId];
require(_user != address(0), "Unknown live ticket");
// signal decrease of stakingAmount
stakingThales.decreaseAndTransferStakedThales(_user, _buyInAmount);
}
/// @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 this contract balance
function confirmLiveTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount) external notPaused nonReentrant {
require(msg.sender == address(liveTradingProcessor), "Only callable from LiveTradingProcessor");
address _user = liveRequestsPerUser[requestId];
require(_user != address(0), "Unknown live ticket");
ticketToUser[_createdTicket] = _user;
activeTicketsPerUser[_user].add(_createdTicket);
emit StakingTokensTrade(_createdTicket, _buyInAmount, _user, true);
}
/// @notice callback from sportsAMM on ticket exercize if owner is this contract. The net winnings are sent to the user's staked balance on the staking contract
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();
if (_exercized > 0) {
stakingThales.increaseAndTransferStakedThales(_user, _exercized);
}
emit StakingTokensTicketResolved(_resolvedTicket, _user, _exercized);
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);
}
/* ========== 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;
}
/* ========== SETTERS ========== */
/// @notice sets new StakingThales address
/// @param _stakingThales new staking thales address
function setStakingThales(address _stakingThales) external onlyOwner {
if (address(stakingThales) != address(0) && address(stakingCollateral) != address(0)) {
stakingCollateral.approve(address(stakingThales), 0);
}
stakingThales = IStakingThales(_stakingThales);
if (_stakingThales != address(0) && address(stakingCollateral) != address(0)) {
stakingCollateral.approve(_stakingThales, MAX_APPROVAL);
}
emit SetStakingThales(_stakingThales);
}
/// @notice sets new SportsAMM
/// @param _sportsAMM new sportsAMM address
function setSportsAMM(address _sportsAMM) external onlyOwner {
if (address(sportsAMM) != address(0) && address(stakingCollateral) != address(0)) {
stakingCollateral.approve(address(sportsAMM), 0);
}
sportsAMM = ISportsAMMV2(_sportsAMM);
if (_sportsAMM != address(0) && address(stakingCollateral) != address(0)) {
stakingCollateral.approve(_sportsAMM, MAX_APPROVAL);
}
emit SetSportsAMM(_sportsAMM);
}
/// @notice sets new LiveTradingProcessor
/// @param _liveTradingProcessor new liveTradingProcessor address
function setLiveTradingProcessor(address _liveTradingProcessor) external onlyOwner {
if (address(liveTradingProcessor) != address(0) && address(stakingCollateral) != address(0)) {
stakingCollateral.approve(address(liveTradingProcessor), 0);
}
liveTradingProcessor = ILiveTradingProcessor(_liveTradingProcessor);
if (_liveTradingProcessor != address(0) && address(stakingCollateral) != address(0)) {
stakingCollateral.approve(_liveTradingProcessor, MAX_APPROVAL);
}
emit SetLiveTradingProcessor(_liveTradingProcessor);
}
/// @notice sets new Staking collateral
/// @param _stakingCollateral new stakingCollateral address
function setStakingCollateral(address _stakingCollateral) external onlyOwner {
if (address(stakingCollateral) != address(0)) {
stakingCollateral.approve(address(stakingThales), 0);
stakingCollateral.approve(address(sportsAMM), 0);
stakingCollateral.approve(address(liveTradingProcessor), 0);
}
stakingCollateral = IERC20(_stakingCollateral);
if (_stakingCollateral != address(0)) {
stakingCollateral.approve(address(stakingThales), MAX_APPROVAL);
stakingCollateral.approve(address(sportsAMM), MAX_APPROVAL);
stakingCollateral.approve(address(liveTradingProcessor), MAX_APPROVAL);
}
emit SetStakingCollateral(_stakingCollateral);
}
/* ========== EVENTS ========== */
event StakingTokensTrade(address createdTicket, uint buyInAmount, address user, bool isLive);
event StakingTokensTicketResolved(address ticket, address user, uint earned);
event StakingTokensLiveTradeRequested(address user, uint buyInAmount, bytes32 requestId);
event SetStakingThales(address stakingThales);
event SetSportsAMM(address sportsAMM);
event SetLiveTradingProcessor(address liveTradingProcessor);
event SetStakingCollateral(address stakingCollateral);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16;
interface IStakingThales {
function updateVolume(address account, uint amount) external;
function updateStakingRewards(
uint _currentPeriodRewards,
uint _extraRewards,
uint _revShare
) external;
/* ========== VIEWS / VARIABLES ========== */
function totalStakedAmount() external view returns (uint);
function stakedBalanceOf(address account) external view returns (uint);
function currentPeriodRewards() external view returns (uint);
function currentPeriodFees() external view returns (uint);
function getLastPeriodOfClaimedRewards(address account) external view returns (uint);
function getRewardsAvailable(address account) external view returns (uint);
function getRewardFeesAvailable(address account) external view returns (uint);
function getAlreadyClaimedRewards(address account) external view returns (uint);
function getContractRewardFunds() external view returns (uint);
function getContractFeeFunds() external view returns (uint);
function getAMMVolume(address account) external view returns (uint);
function decreaseAndTransferStakedThales(address account, uint amount) external;
function increaseAndTransferStakedThales(address account, uint amount) external;
function updateVolumeAtAmountDecimals(
address account,
uint amount,
uint decimals
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// internal
import "../../interfaces/ISportsAMMV2Manager.sol";
import "../../interfaces/ISportsAMMV2.sol";
contract Ticket {
using SafeERC20 for IERC20;
uint private constant ONE = 1e18;
enum Phase {
Trading,
Maturity,
Expiry
}
struct MarketData {
bytes32 gameId;
uint16 sportId;
uint16 typeId;
uint maturity;
uint8 status;
int24 line;
uint24 playerId;
uint8 position;
uint odd;
ISportsAMMV2.CombinedPosition[] combinedPositions;
}
struct TicketInit {
MarketData[] _markets;
uint _buyInAmount;
uint _fees;
uint _totalQuote;
address _sportsAMM;
address _ticketOwner;
IERC20 _collateral;
uint _expiry;
bool _isLive;
uint8 _systemBetDenominator;
}
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;
/* ========== CONSTRUCTOR ========== */
/// @notice initialize the ticket contract
/// @param params all parameters for Init
function initialize(TicketInit calldata params) external {
require(!initialized, "Ticket already initialized");
initialized = true;
sportsAMM = ISportsAMMV2(params._sportsAMM);
numOfMarkets = params._markets.length;
for (uint i = 0; i < numOfMarkets; i++) {
markets[i] = params._markets[i];
}
buyInAmount = params._buyInAmount;
fees = params._fees;
totalQuote = params._totalQuote;
ticketOwner = params._ticketOwner;
collateral = params._collateral;
expiry = params._expiry;
isLive = params._isLive;
createdAt = block.timestamp;
systemBetDenominator = params._systemBetDenominator;
isSystem = systemBetDenominator > 0;
}
/* ========== EXTERNAL READ FUNCTIONS ========== */
/// @notice checks if the user lost the ticket
/// @return isTicketLost true/false
function isTicketLost() public view returns (bool) {
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 returns (uint) {
require(!paused, "Market paused");
bool isExercisable = isTicketExercisable();
require(isExercisable, "Ticket not exercisable yet");
uint payoutWithFees = collateral.balanceOf(address(this));
uint payout = payoutWithFees - fees;
bool isCancelled = false;
if (_isUserTheWinner()) {
finalPayout = payout;
isCancelled = true;
for (uint i = 0; i < numOfMarkets; i++) {
bool isCancelledMarketPosition = sportsAMM.resultManager().isCancelledMarketPosition(
markets[i].gameId,
markets[i].typeId,
markets[i].playerId,
markets[i].line,
markets[i].position,
markets[i].combinedPositions
);
if (isCancelledMarketPosition) {
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 returns (uint) {
require(!paused, "Market paused");
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 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");
_;
}
/* ========== EVENTS ========== */
event Resolved(bool isUserTheWinner, bool cancelled);
event Expired(address beneficiary);
event PauseUpdated(bool paused);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IProxyBetting.sol";
interface IFreeBetsHolder is IProxyBetting {
function confirmLiveTrade(bytes32 requestId, address _createdTicket, uint _buyInAmount, address _collateral) external;
}// 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;
}
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 "@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 {
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 exerciseTicket(address _ticket) 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 rootPerGame(bytes32 game) external view returns (bytes32);
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 isSystemTicket(address _ticket) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./ISportsAMMV2.sol";
interface ISportsAMMV2ResultManager {
enum MarketPositionStatus {
Open,
Cancelled,
Winning,
Losing
}
function isMarketResolved(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
ISportsAMMV2.CombinedPosition[] memory combinedPositions
) external view returns (bool isResolved);
function getMarketPositionStatus(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (MarketPositionStatus status);
function isWinningMarketPosition(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (bool isWinning);
function isCancelledMarketPosition(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (bool isCancelled);
function getResultsPerMarket(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId
) external view returns (int24[] memory results);
function resultTypePerMarketType(uint _typeId) external view returns (uint8 marketType);
function isMarketResolvedAndPositionWinning(
bytes32 _gameId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _position,
ISportsAMMV2.CombinedPosition[] memory _combinedPositions
) external view returns (bool isResolved, bool isWinning);
function setResultsPerMarkets(
bytes32[] memory _gameIds,
uint16[] memory _typeIds,
uint24[] memory _playerIds,
int24[][] memory _results
) external;
function isGameCancelled(bytes32 _gameId) external view returns (bool);
function cancelGames(bytes32[] memory _gameIds) external;
function cancelMarkets(
bytes32[] memory _gameIds,
uint16[] memory _typeIds,
uint24[] memory _playerIds,
int24[] memory _lines
) external;
function cancelMarket(bytes32 _gameId, uint16 _typeId, uint24 _playerId, int24 _line) external;
function cancelGame(bytes32 _gameId) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./ISportsAMMV2.sol";
interface ISportsAMMV2RiskManager {
struct TypeCap {
uint typeId;
uint cap;
}
struct CapData {
uint capPerSport;
uint capPerChild;
TypeCap[] capPerType;
}
struct DynamicLiquidityData {
uint cutoffTimePerSport;
uint cutoffDividerPerSport;
}
struct RiskData {
uint sportId;
CapData capData;
uint riskMultiplierPerSport;
DynamicLiquidityData dynamicLiquidityData;
}
enum RiskStatus {
NoRisk,
OutOfLiquidity,
InvalidCombination
}
function minBuyInAmount() external view returns (uint);
function maxTicketSize() external view returns (uint);
function maxSupportedAmount() external view returns (uint);
function maxSupportedOdds() external view returns (uint);
function maxAllowedSystemCombinations() external view returns (uint);
function expiryDuration() external view returns (uint);
function liveTradingPerSportAndTypeEnabled(uint _sportId, uint _typeId) external view returns (bool _enabled);
function calculateCapToBeUsed(
bytes32 _gameId,
uint16 _sportId,
uint16 _typeId,
uint24 _playerId,
int24 _line,
uint _maturity,
bool _isLive
) external view returns (uint cap);
function checkRisks(
ISportsAMMV2.TradeData[] memory _tradeData,
uint _buyInAmount,
bool _isLive
) 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 checkAndUpdateRisks(ISportsAMMV2.TradeData[] memory _tradeData, uint _buyInAmount, bool _isLive) external;
function verifyMerkleTree(ISportsAMMV2.TradeData memory _marketTradeData, bytes32 _rootPerGame) external pure;
function isSportIdFuture(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;
}// 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": 200
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}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":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"isPaused","type":"bool"}],"name":"PauseChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liveTradingProcessor","type":"address"}],"name":"SetLiveTradingProcessor","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":"stakingCollateral","type":"address"}],"name":"SetStakingCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"stakingThales","type":"address"}],"name":"SetStakingThales","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":"StakingTokensLiveTradeRequested","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":"StakingTokensTicketResolved","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":"StakingTokensTrade","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"address","name":"_createdTicket","type":"address"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"}],"name":"confirmLiveTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_resolvedTicket","type":"address"}],"name":"confirmTicketResolved","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":[],"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"},{"internalType":"address","name":"_stakingThales","type":"address"},{"internalType":"address","name":"_stakingToken","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","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":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"requestId","type":"bytes32"},{"internalType":"uint256","name":"_buyInAmount","type":"uint256"}],"name":"preConfirmLiveTrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_collateral","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"retrieveFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_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":"_sportsAMM","type":"address"}],"name":"setSportsAMM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakingCollateral","type":"address"}],"name":"setStakingCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakingThales","type":"address"}],"name":"setStakingThales","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sportsAMM","outputs":[{"internalType":"contract ISportsAMMV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingCollateral","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingThales","outputs":[{"internalType":"contract IStakingThales","name":"","type":"address"}],"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"}],"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":"struct ILiveTradingProcessor.LiveTradeData","name":"_liveTradeData","type":"tuple"}],"name":"tradeLive","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":"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
608060405234801561001057600080fd5b50612f23806100206000396000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c80638da5cb5b1161010f578063c0d2600c116100a2578063dd37861d11610071578063dd37861d14610495578063e81e52ee146104a8578063ebc79772146104bb578063fd8a8cc6146104c357600080fd5b8063c0d2600c14610444578063c3b83f5f14610457578063c99252881461046a578063d9a7951f1461048257600080fd5b806394bd2313116100de57806394bd2313146103f8578063a13d6b6a1461040b578063af82a32a1461041e578063b42c8afb1461043157600080fd5b80638da5cb5b1461038a5780638f974cd91461039d5780639168fdd1146103c657806391b4ded9146103ef57600080fd5b80632912dfc8116101875780635c975abb116101565780635c975abb1461033f5780635ed9da7d1461035c57806379ba50971461036f5780637f8525821461037757600080fd5b80632912dfc8146102d05780633a572121146102e3578063485b23bf1461030c57806353a47bb71461032c57600080fd5b80631590a4a4116101c35780631590a4a4146102605780631627540c1461029757806316c38b3c146102aa578063188aa45d146102bd57600080fd5b806304143d31146101f557806304de96c51461020a57806313af40351461023a5780631459457a1461024d575b600080fd5b61020861020336600461241b565b6104d6565b005b60065461021d906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61020861024836600461247c565b610668565b61020861025b366004612499565b610799565b61028961026e36600461247c565b6001600160a01b03166000908152600b602052604090205490565b604051908152602001610231565b6102086102a536600461247c565b610a68565b6102086102b8366004612518565b610abe565b6102086102cb366004612535565b610b30565b6102086102de366004612561565b610b50565b61021d6102f1366004612583565b600a602052600090815260409020546001600160a01b031681565b61031f61031a36600461259c565b610c9e565b60405161023191906125d5565b60015461021d906001600160a01b031681565b60035461034c9060ff1681565b6040519015158152602001610231565b61020861036a36600461266e565b610cce565b610208610d46565b61020861038536600461247c565b610e30565b60005461021d906001600160a01b031681565b61021d6103ab36600461247c565b6009602052600090815260409020546001600160a01b031681565b6102896103d436600461247c565b6001600160a01b03166000908152600c602052604090205490565b61028960025481565b61020861040636600461247c565b610fca565b61020861041936600461247c565b611164565b60085461021d906001600160a01b031681565b61020861043f3660046126f2565b6113e1565b61020861045236600461247c565b611459565b61020861046536600461247c565b6117c3565b60055461021d9061010090046001600160a01b031681565b610208610490366004612775565b6118cc565b61031f6104a336600461259c565b611a4a565b6102086104b636600461247c565b611a70565b610208611c1e565b60075461021d906001600160a01b031681565b60035460ff16156105025760405162461bcd60e51b81526004016104f9906127ad565b60405180910390fd5b6008546001600160a01b03166105206101408301610120840161247c565b6001600160a01b0316146105845760405162461bcd60e51b815260206004820152602560248201527f557365205374616b696e6720636f6c6c61746572616c20666f72206c69766520604482015264747261646560d81b60648201526084016104f9565b600654604051631a921eb960e21b81526000916001600160a01b031690636a487ae4906105b590859060040161289d565b6020604051808303816000875af11580156105d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f8919061299d565b6000818152600a602090815260409182902080546001600160a01b03191633908117909155825190815260a0860135918101919091529081018290529091507f06809a073d838c2858dfed590fabd8a8496d18b91be36cb574324c0d4d46dde69060600160405180910390a15050565b6001600160a01b0381166106be5760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f7420626520300000000000000060448201526064016104f9565b600154600160a01b900460ff161561072a5760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b60648201526084016104f9565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b0319909116811782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91015b60405180910390a150565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff166000811580156107df5750825b905060008267ffffffffffffffff1660011480156107fc5750303b155b90508115801561080a575080155b156108285760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561085257845460ff60401b1916600160401b1785555b61085b8a610668565b610863611c1e565b600580546001600160a01b03808c1661010002610100600160a81b031990921691909117909155600680548a83166001600160a01b031991821617909155600780548a841690831617905560088054928916929091168217905560405163095ea7b360e01b815263095ea7b3906108e2908a90600019906004016129b6565b6020604051808303816000875af1158015610901573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061092591906129cf565b5060085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061095a908c90600019906004016129b6565b6020604051808303816000875af1158015610979573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099d91906129cf565b5060085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b3906109d2908b90600019906004016129b6565b6020604051808303816000875af11580156109f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1591906129cf565b508315610a5c57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b610a70611c7c565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200161078e565b610ac6611c7c565b60035460ff16151581151514610b2d576003805460ff191682151590811790915560ff1615610af457426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec59060200161078e565b50565b610b38611c7c565b610b4c6001600160a01b0383163383611cf0565b5050565b60035460ff1615610b735760405162461bcd60e51b81526004016104f9906127ad565b600160046000828254610b869190612a02565b90915550506004546006546001600160a01b03163314610bb85760405162461bcd60e51b81526004016104f990612a15565b6000838152600a60205260409020546001600160a01b031680610c135760405162461bcd60e51b8152602060048201526013602482015272155b9adb9bdddb881b1a5d99481d1a58dad95d606a1b60448201526064016104f9565b60075460405163605f5aa960e01b81526001600160a01b039091169063605f5aa990610c4590849087906004016129b6565b600060405180830381600087803b158015610c5f57600080fd5b505af1158015610c73573d6000803e3d6000fd5b50505050506004548114610c995760405162461bcd60e51b81526004016104f990612a5c565b505050565b6001600160a01b0381166000908152600c60205260409020606090610cc4908585611d48565b90505b9392505050565b60035460ff1615610cf15760405162461bcd60e51b81526004016104f9906127ad565b600160046000828254610d049190612a02565b9091555050600454610d1c8787878787876000611e63565b6004548114610d3d5760405162461bcd60e51b81526004016104f990612a5c565b50505050505050565b6001546001600160a01b03163314610dbe5760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b60648201526084016104f9565b600054600154604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b610e38611c7c565b6007546001600160a01b031615801590610e5c57506008546001600160a01b031615155b15610edd5760085460075460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392610e98929116906000906004016129b6565b6020604051808303816000875af1158015610eb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610edb91906129cf565b505b600780546001600160a01b0319166001600160a01b03831690811790915515801590610f1357506008546001600160a01b031615155b15610f915760085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610f4c908490600019906004016129b6565b6020604051808303816000875af1158015610f6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8f91906129cf565b505b6040516001600160a01b03821681527f475a2179b9b6a155e7ba3f46a461beb6798279b265026364944e38dcb4bacafe9060200161078e565b610fd2611c7c565b6006546001600160a01b031615801590610ff657506008546001600160a01b031615155b156110775760085460065460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392611032929116906000906004016129b6565b6020604051808303816000875af1158015611051573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061107591906129cf565b505b600680546001600160a01b0319166001600160a01b038316908117909155158015906110ad57506008546001600160a01b031615155b1561112b5760085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b3906110e6908490600019906004016129b6565b6020604051808303816000875af1158015611105573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112991906129cf565b505b6040516001600160a01b03821681527f8b5670b3bc5c520b7a3580c753ba88065a561ad52a99674196859b044ea202859060200161078e565b60055461010090046001600160a01b031633146111c35760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c7920616c6c6f7765642066726f6d2053706f727473414d4d000000000060448201526064016104f9565b6001600160a01b03808216600090815260096020526040902054168061121c5760405162461bcd60e51b815260206004820152600e60248201526d155b9adb9bdddb881d1a58dad95d60921b60448201526064016104f9565b6001600160a01b0381166000908152600b6020526040902061123e908361208e565b6112825760405162461bcd60e51b8152602060048201526015602482015274155b9adb9bdddb881858dd1a5d99481d1a58dad95d605a1b60448201526064016104f9565b6000826001600160a01b031663242a8a6b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e6919061299d565b905080156113525760075460405162eff4a960e81b81526001600160a01b039091169063eff4a9009061131f90859085906004016129b6565b600060405180830381600087803b15801561133957600080fd5b505af115801561134d573d6000803e3d6000fd5b505050505b604080516001600160a01b038086168252841660208201529081018290527f42ff25c4a1eb82e8fd21db8473b1e7f760a7ce0e19051ba95ca3f5096d490dba9060600160405180910390a16001600160a01b0382166000908152600b602052604090206113bf9084612106565b6001600160a01b0382166000908152600c60205260409020610c99908461225f565b60035460ff16156114045760405162461bcd60e51b81526004016104f9906127ad565b6001600460008282546114179190612a02565b909155505060045461142e88888888888888611e63565b600454811461144f5760405162461bcd60e51b81526004016104f990612a5c565b5050505050505050565b611461611c7c565b6008546001600160a01b0316156115eb5760085460075460405163095ea7b360e01b81526001600160a01b039283169263095ea7b3926114a9929116906000906004016129b6565b6020604051808303816000875af11580156114c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ec91906129cf565b5060085460055460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261152b92610100909104909116906000906004016129b6565b6020604051808303816000875af115801561154a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156e91906129cf565b5060085460065460405163095ea7b360e01b81526001600160a01b039283169263095ea7b3926115a6929116906000906004016129b6565b6020604051808303816000875af11580156115c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e991906129cf565b505b600880546001600160a01b0319166001600160a01b0383169081179091551561178a5760085460075460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261164692911690600019906004016129b6565b6020604051808303816000875af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168991906129cf565b5060085460055460405163095ea7b360e01b81526001600160a01b039283169263095ea7b3926116c99261010090910490911690600019906004016129b6565b6020604051808303816000875af11580156116e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170c91906129cf565b5060085460065460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261174592911690600019906004016129b6565b6020604051808303816000875af1158015611764573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178891906129cf565b505b6040516001600160a01b03821681527f66494eb14734ccacf6e5eee35599b9bf492807d93c14985e00b959eb2f651fed9060200161078e565b6117cb611c7c565b6001600160a01b0381166118135760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016104f9565b600154600160a81b900460ff16156118635760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b60448201526064016104f9565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b1790556040805182815260208101929092527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910161078e565b60035460ff16156118ef5760405162461bcd60e51b81526004016104f9906127ad565b6001600460008282546119029190612a02565b90915550506004546006546001600160a01b031633146119345760405162461bcd60e51b81526004016104f990612a15565b6000848152600a60205260409020546001600160a01b03168061198f5760405162461bcd60e51b8152602060048201526013602482015272155b9adb9bdddb881b1a5d99481d1a58dad95d606a1b60448201526064016104f9565b6001600160a01b03848116600090815260096020908152604080832080546001600160a01b0319169486169485179055928252600b9052206119d1908561225f565b604080516001600160a01b038681168252602082018690528316818301526001606082015290517f7aaa2d826ef55c1cbb66bd84ac4b232ee95588980b3800ff76f23e286a8bcd359181900360800190a1506004548114611a445760405162461bcd60e51b81526004016104f990612a5c565b50505050565b6001600160a01b0381166000908152600b60205260409020606090610cc4908585611d48565b611a78611c7c565b60055461010090046001600160a01b031615801590611aa157506008546001600160a01b031615155b15611b295760085460055460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392611ae492610100909104909116906000906004016129b6565b6020604051808303816000875af1158015611b03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b2791906129cf565b505b60058054610100600160a81b0319166101006001600160a01b0384169081029190911790915515801590611b6757506008546001600160a01b031615155b15611be55760085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390611ba0908490600019906004016129b6565b6020604051808303816000875af1158015611bbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be391906129cf565b505b6040516001600160a01b03821681527f9985022676a73860c32a3b91ea7a7dfe2d5e87c148f50eb519d8b0f33ab7f8b99060200161078e565b60055460ff1615611c675760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016104f9565b6005805460ff19166001908117909155600455565b6000546001600160a01b03163314611cee5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b60648201526084016104f9565b565b610c9983846001600160a01b031663a9059cbb8585604051602401611d169291906129b6565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506122b1565b60606000611d568385612a02565b8554909150811115611d66575083545b838111611d83575050604080516000815260208101909152610cc7565b6000611d8f8583612aa9565b905060008167ffffffffffffffff811115611dac57611dac612a93565b604051908082528060200260200182016040528015611dd5578160200160208202803683370190505b50905060005b82811015611e585787611dee8883612a02565b81548110611dfe57611dfe612abc565b9060005260206000200160009054906101000a90046001600160a01b0316828281518110611e2e57611e2e612abc565b6001600160a01b039092166020928302919091019091015280611e5081612ad2565b915050611ddb565b509695505050505050565b60075460405163605f5aa960e01b81526001600160a01b039091169063605f5aa990611e9590339089906004016129b6565b600060405180830381600087803b158015611eaf57600080fd5b505af1158015611ec3573d6000803e3d6000fd5b5060009250505060ff821615611f67576005546008546040516349d8615760e11b81526001600160a01b036101009093048316926393b0c2ae92611f1d928d928d928d928d928d928d929116906000908d90600401612dde565b6020604051808303816000875af1158015611f3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f609190612e3a565b9050611ff4565b6005546008546040516214e17b60e41b81526001600160a01b0361010090930483169263014e17b092611fae928d928d928d928d928d928d92911690600090600401612e57565b6020604051808303816000875af1158015611fcd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ff19190612e3a565b90505b6001600160a01b038116600090815260096020908152604080832080546001600160a01b031916339081179091558352600b9091529020612035908261225f565b604080516001600160a01b03831681526020810188905233818301526000606082015290517f7aaa2d826ef55c1cbb66bd84ac4b232ee95588980b3800ff76f23e286a8bcd359181900360800190a15050505050505050565b815460009081036120a157506000612100565b6001600160a01b0382166000908152600184016020526040902054801515806120fc5750826001600160a01b0316846000016000815481106120e5576120e5612abc565b6000918252602090912001546001600160a01b0316145b9150505b92915050565b612110828261208e565b6121525760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b60448201526064016104f9565b6001600160a01b038116600090815260018084016020526040822054845490929161217c91612aa9565b905080821461220857600084600001828154811061219c5761219c612abc565b60009182526020909120015485546001600160a01b03909116915081908690859081106121cb576121cb612abc565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b835484908061221957612219612ea8565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b612269828261208e565b610b4c5781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555050565b60006122c66001600160a01b03841683612314565b905080516000141580156122eb5750808060200190518101906122e991906129cf565b155b15610c9957604051635274afe760e01b81526001600160a01b03841660048201526024016104f9565b6060610cc78383600084600080856001600160a01b0316848660405161233a9190612ebe565b60006040518083038185875af1925050503d8060008114612377576040519150601f19603f3d011682016040523d82523d6000602084013e61237c565b606091505b509150915061238c868383612396565b9695505050505050565b6060826123ab576123a6826123f2565b610cc7565b81511580156123c257506001600160a01b0384163b155b156123eb57604051639996b31560e01b81526001600160a01b03851660048201526024016104f9565b5080610cc7565b8051156124025780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60006020828403121561242d57600080fd5b813567ffffffffffffffff81111561244457600080fd5b82016101408185031215610cc757600080fd5b6001600160a01b0381168114610b2d57600080fd5b803561247781612457565b919050565b60006020828403121561248e57600080fd5b8135610cc781612457565b600080600080600060a086880312156124b157600080fd5b85356124bc81612457565b945060208601356124cc81612457565b935060408601356124dc81612457565b925060608601356124ec81612457565b915060808601356124fc81612457565b809150509295509295909350565b8015158114610b2d57600080fd5b60006020828403121561252a57600080fd5b8135610cc78161250a565b6000806040838503121561254857600080fd5b823561255381612457565b946020939093013593505050565b6000806040838503121561257457600080fd5b50508035926020909101359150565b60006020828403121561259557600080fd5b5035919050565b6000806000606084860312156125b157600080fd5b833592506020840135915060408401356125ca81612457565b809150509250925092565b6020808252825182820181905260009190848201906040850190845b818110156126165783516001600160a01b0316835292840192918401916001016125f1565b50909695505050505050565b60008083601f84011261263457600080fd5b50813567ffffffffffffffff81111561264c57600080fd5b6020830191508360208260051b850101111561266757600080fd5b9250929050565b60008060008060008060a0878903121561268757600080fd5b863567ffffffffffffffff81111561269e57600080fd5b6126aa89828a01612622565b90975095505060208701359350604087013592506060870135915060808701356126d381612457565b809150509295509295509295565b803560ff8116811461247757600080fd5b600080600080600080600060c0888a03121561270d57600080fd5b873567ffffffffffffffff81111561272457600080fd5b6127308a828b01612622565b909850965050602088013594506040880135935060608801359250608088013561275981612457565b915061276760a089016126e1565b905092959891949750929550565b60008060006060848603121561278a57600080fd5b83359250602084013561279c81612457565b929592945050506040919091013590565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b6000808335601e1984360301811261282157600080fd5b830160208101925035905067ffffffffffffffff81111561284157600080fd5b80360382131561266757600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b803561ffff8116811461247757600080fd5b8035600281900b811461247757600080fd5b6020815260006128ad838461280a565b6101408060208601526128c561016086018385612850565b92506128d360208701612879565b61ffff8116604087015291506128eb60408701612879565b61ffff8116606087015291506129036060870161288b565b9150612914608086018360020b9052565b612920608087016126e1565b60ff811660a0870152915060a086013560c086015260c086013560e0860152610100915060e08601358286015261295882870161246c565b9150610120612971818701846001600160a01b03169052565b61297c81880161246c565b925050612993818601836001600160a01b03169052565b5090949350505050565b6000602082840312156129af57600080fd5b5051919050565b6001600160a01b03929092168252602082015260400190565b6000602082840312156129e157600080fd5b8151610cc78161250a565b634e487b7160e01b600052601160045260246000fd5b80820180821115612100576121006129ec565b60208082526027908201527f4f6e6c792063616c6c61626c652066726f6d204c69766554726164696e67507260408201526637b1b2b9b9b7b960c91b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b600052604160045260246000fd5b81810381811115612100576121006129ec565b634e487b7160e01b600052603260045260246000fd5b600060018201612ae457612ae46129ec565b5060010190565b803562ffffff8116811461247757600080fd5b6000808335601e19843603018112612b1557600080fd5b830160208101925035905067ffffffffffffffff811115612b3557600080fd5b8060051b360382131561266757600080fd5b81835260006001600160fb1b03831115612b6057600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b85811015612bd95761ffff612b9d83612879565b16875260ff612bad8484016126e1565b16838801526040612bbf81840161288b565b60020b908801526060968701969190910190600101612b89565b509495945050505050565b81835260006020808501808196508560051b810191508460005b87811015612c6c5782840389528135601e19883603018112612c1f57600080fd5b8701858101903567ffffffffffffffff811115612c3b57600080fd5b606081023603821315612c4d57600080fd5b612c58868284612b79565b9a87019a9550505090840190600101612bfe565b5091979650505050505050565b81835260006020808501808196508560051b81019150846000805b88811015612dd0578385038a52823561015e19893603018112612cb5578283fd5b880180358652610160612cc9888301612879565b61ffff16888801526040612cde838201612879565b61ffff1690880152606082810135908801526080612cfd8184016126e1565b60ff169088015260a0612d1183820161288b565b612d1f828a018260020b9052565b505060c0612d2e818401612aeb565b62ffffff169088015260e0612d4583820184612afe565b83838b0152612d57848b018284612b47565b9350505050610100612d6b81840184612afe565b898403838b0152612d7d848284612b47565b9350505050610120612d908184016126e1565b60ff1690880152610140612da683820184612afe565b9350888303828a0152612dba838583612be4565b9d8a019d98505050938701935050600101612c94565b509298975050505050505050565b6000610100808352612df38184018c8e612c79565b602084019a909a525050604081019690965260608601949094526001600160a01b039283166080860152911660a0840152151560c083015260ff1660e09091015292915050565b600060208284031215612e4c57600080fd5b8151610cc781612457565b60e081526000612e6b60e083018a8c612c79565b602083019890985250604081019590955260608501939093526001600160a01b0391821660808501521660a0830152151560c09091015292915050565b634e487b7160e01b600052603160045260246000fd5b6000825160005b81811015612edf5760208186018101518583015201612ec5565b50600092019182525091905056fea2646970667358221220aca66c3927b3c5cb53998e79eeeede24514f342afa95697e7d1d87c00a77bde064736f6c63430008140033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101f05760003560e01c80638da5cb5b1161010f578063c0d2600c116100a2578063dd37861d11610071578063dd37861d14610495578063e81e52ee146104a8578063ebc79772146104bb578063fd8a8cc6146104c357600080fd5b8063c0d2600c14610444578063c3b83f5f14610457578063c99252881461046a578063d9a7951f1461048257600080fd5b806394bd2313116100de57806394bd2313146103f8578063a13d6b6a1461040b578063af82a32a1461041e578063b42c8afb1461043157600080fd5b80638da5cb5b1461038a5780638f974cd91461039d5780639168fdd1146103c657806391b4ded9146103ef57600080fd5b80632912dfc8116101875780635c975abb116101565780635c975abb1461033f5780635ed9da7d1461035c57806379ba50971461036f5780637f8525821461037757600080fd5b80632912dfc8146102d05780633a572121146102e3578063485b23bf1461030c57806353a47bb71461032c57600080fd5b80631590a4a4116101c35780631590a4a4146102605780631627540c1461029757806316c38b3c146102aa578063188aa45d146102bd57600080fd5b806304143d31146101f557806304de96c51461020a57806313af40351461023a5780631459457a1461024d575b600080fd5b61020861020336600461241b565b6104d6565b005b60065461021d906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61020861024836600461247c565b610668565b61020861025b366004612499565b610799565b61028961026e36600461247c565b6001600160a01b03166000908152600b602052604090205490565b604051908152602001610231565b6102086102a536600461247c565b610a68565b6102086102b8366004612518565b610abe565b6102086102cb366004612535565b610b30565b6102086102de366004612561565b610b50565b61021d6102f1366004612583565b600a602052600090815260409020546001600160a01b031681565b61031f61031a36600461259c565b610c9e565b60405161023191906125d5565b60015461021d906001600160a01b031681565b60035461034c9060ff1681565b6040519015158152602001610231565b61020861036a36600461266e565b610cce565b610208610d46565b61020861038536600461247c565b610e30565b60005461021d906001600160a01b031681565b61021d6103ab36600461247c565b6009602052600090815260409020546001600160a01b031681565b6102896103d436600461247c565b6001600160a01b03166000908152600c602052604090205490565b61028960025481565b61020861040636600461247c565b610fca565b61020861041936600461247c565b611164565b60085461021d906001600160a01b031681565b61020861043f3660046126f2565b6113e1565b61020861045236600461247c565b611459565b61020861046536600461247c565b6117c3565b60055461021d9061010090046001600160a01b031681565b610208610490366004612775565b6118cc565b61031f6104a336600461259c565b611a4a565b6102086104b636600461247c565b611a70565b610208611c1e565b60075461021d906001600160a01b031681565b60035460ff16156105025760405162461bcd60e51b81526004016104f9906127ad565b60405180910390fd5b6008546001600160a01b03166105206101408301610120840161247c565b6001600160a01b0316146105845760405162461bcd60e51b815260206004820152602560248201527f557365205374616b696e6720636f6c6c61746572616c20666f72206c69766520604482015264747261646560d81b60648201526084016104f9565b600654604051631a921eb960e21b81526000916001600160a01b031690636a487ae4906105b590859060040161289d565b6020604051808303816000875af11580156105d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f8919061299d565b6000818152600a602090815260409182902080546001600160a01b03191633908117909155825190815260a0860135918101919091529081018290529091507f06809a073d838c2858dfed590fabd8a8496d18b91be36cb574324c0d4d46dde69060600160405180910390a15050565b6001600160a01b0381166106be5760405162461bcd60e51b815260206004820152601960248201527f4f776e657220616464726573732063616e6e6f7420626520300000000000000060448201526064016104f9565b600154600160a01b900460ff161561072a5760405162461bcd60e51b815260206004820152602960248201527f416c726561647920696e697469616c697a65642c20757365206e6f6d696e617460448201526832a732bba7bbb732b960b91b60648201526084016104f9565b6001805460ff60a01b1916600160a01b179055600080546001600160a01b0383166001600160a01b0319909116811782556040805192835260208301919091527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c91015b60405180910390a150565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff166000811580156107df5750825b905060008267ffffffffffffffff1660011480156107fc5750303b155b90508115801561080a575080155b156108285760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561085257845460ff60401b1916600160401b1785555b61085b8a610668565b610863611c1e565b600580546001600160a01b03808c1661010002610100600160a81b031990921691909117909155600680548a83166001600160a01b031991821617909155600780548a841690831617905560088054928916929091168217905560405163095ea7b360e01b815263095ea7b3906108e2908a90600019906004016129b6565b6020604051808303816000875af1158015610901573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061092591906129cf565b5060085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b39061095a908c90600019906004016129b6565b6020604051808303816000875af1158015610979573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099d91906129cf565b5060085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b3906109d2908b90600019906004016129b6565b6020604051808303816000875af11580156109f1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a1591906129cf565b508315610a5c57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b610a70611c7c565b600180546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200161078e565b610ac6611c7c565b60035460ff16151581151514610b2d576003805460ff191682151590811790915560ff1615610af457426002555b60035460405160ff909116151581527f8fb6c181ee25a520cf3dd6565006ef91229fcfe5a989566c2a3b8c115570cec59060200161078e565b50565b610b38611c7c565b610b4c6001600160a01b0383163383611cf0565b5050565b60035460ff1615610b735760405162461bcd60e51b81526004016104f9906127ad565b600160046000828254610b869190612a02565b90915550506004546006546001600160a01b03163314610bb85760405162461bcd60e51b81526004016104f990612a15565b6000838152600a60205260409020546001600160a01b031680610c135760405162461bcd60e51b8152602060048201526013602482015272155b9adb9bdddb881b1a5d99481d1a58dad95d606a1b60448201526064016104f9565b60075460405163605f5aa960e01b81526001600160a01b039091169063605f5aa990610c4590849087906004016129b6565b600060405180830381600087803b158015610c5f57600080fd5b505af1158015610c73573d6000803e3d6000fd5b50505050506004548114610c995760405162461bcd60e51b81526004016104f990612a5c565b505050565b6001600160a01b0381166000908152600c60205260409020606090610cc4908585611d48565b90505b9392505050565b60035460ff1615610cf15760405162461bcd60e51b81526004016104f9906127ad565b600160046000828254610d049190612a02565b9091555050600454610d1c8787878787876000611e63565b6004548114610d3d5760405162461bcd60e51b81526004016104f990612a5c565b50505050505050565b6001546001600160a01b03163314610dbe5760405162461bcd60e51b815260206004820152603560248201527f596f75206d757374206265206e6f6d696e61746564206265666f726520796f7560448201527402063616e20616363657074206f776e65727368697605c1b60648201526084016104f9565b600054600154604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160018054600080546001600160a01b03199081166001600160a01b03841617909155169055565b610e38611c7c565b6007546001600160a01b031615801590610e5c57506008546001600160a01b031615155b15610edd5760085460075460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392610e98929116906000906004016129b6565b6020604051808303816000875af1158015610eb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610edb91906129cf565b505b600780546001600160a01b0319166001600160a01b03831690811790915515801590610f1357506008546001600160a01b031615155b15610f915760085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610f4c908490600019906004016129b6565b6020604051808303816000875af1158015610f6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8f91906129cf565b505b6040516001600160a01b03821681527f475a2179b9b6a155e7ba3f46a461beb6798279b265026364944e38dcb4bacafe9060200161078e565b610fd2611c7c565b6006546001600160a01b031615801590610ff657506008546001600160a01b031615155b156110775760085460065460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392611032929116906000906004016129b6565b6020604051808303816000875af1158015611051573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061107591906129cf565b505b600680546001600160a01b0319166001600160a01b038316908117909155158015906110ad57506008546001600160a01b031615155b1561112b5760085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b3906110e6908490600019906004016129b6565b6020604051808303816000875af1158015611105573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112991906129cf565b505b6040516001600160a01b03821681527f8b5670b3bc5c520b7a3580c753ba88065a561ad52a99674196859b044ea202859060200161078e565b60055461010090046001600160a01b031633146111c35760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c7920616c6c6f7765642066726f6d2053706f727473414d4d000000000060448201526064016104f9565b6001600160a01b03808216600090815260096020526040902054168061121c5760405162461bcd60e51b815260206004820152600e60248201526d155b9adb9bdddb881d1a58dad95d60921b60448201526064016104f9565b6001600160a01b0381166000908152600b6020526040902061123e908361208e565b6112825760405162461bcd60e51b8152602060048201526015602482015274155b9adb9bdddb881858dd1a5d99481d1a58dad95d605a1b60448201526064016104f9565b6000826001600160a01b031663242a8a6b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e6919061299d565b905080156113525760075460405162eff4a960e81b81526001600160a01b039091169063eff4a9009061131f90859085906004016129b6565b600060405180830381600087803b15801561133957600080fd5b505af115801561134d573d6000803e3d6000fd5b505050505b604080516001600160a01b038086168252841660208201529081018290527f42ff25c4a1eb82e8fd21db8473b1e7f760a7ce0e19051ba95ca3f5096d490dba9060600160405180910390a16001600160a01b0382166000908152600b602052604090206113bf9084612106565b6001600160a01b0382166000908152600c60205260409020610c99908461225f565b60035460ff16156114045760405162461bcd60e51b81526004016104f9906127ad565b6001600460008282546114179190612a02565b909155505060045461142e88888888888888611e63565b600454811461144f5760405162461bcd60e51b81526004016104f990612a5c565b5050505050505050565b611461611c7c565b6008546001600160a01b0316156115eb5760085460075460405163095ea7b360e01b81526001600160a01b039283169263095ea7b3926114a9929116906000906004016129b6565b6020604051808303816000875af11580156114c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ec91906129cf565b5060085460055460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261152b92610100909104909116906000906004016129b6565b6020604051808303816000875af115801561154a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061156e91906129cf565b5060085460065460405163095ea7b360e01b81526001600160a01b039283169263095ea7b3926115a6929116906000906004016129b6565b6020604051808303816000875af11580156115c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e991906129cf565b505b600880546001600160a01b0319166001600160a01b0383169081179091551561178a5760085460075460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261164692911690600019906004016129b6565b6020604051808303816000875af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168991906129cf565b5060085460055460405163095ea7b360e01b81526001600160a01b039283169263095ea7b3926116c99261010090910490911690600019906004016129b6565b6020604051808303816000875af11580156116e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061170c91906129cf565b5060085460065460405163095ea7b360e01b81526001600160a01b039283169263095ea7b39261174592911690600019906004016129b6565b6020604051808303816000875af1158015611764573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178891906129cf565b505b6040516001600160a01b03821681527f66494eb14734ccacf6e5eee35599b9bf492807d93c14985e00b959eb2f651fed9060200161078e565b6117cb611c7c565b6001600160a01b0381166118135760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b60448201526064016104f9565b600154600160a81b900460ff16156118635760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481d1c985b9cd9995c9c9959606a1b60448201526064016104f9565b600080546001600160a01b0383166001600160a01b031990911681179091556001805460ff60a81b1916600160a81b1790556040805182815260208101929092527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910161078e565b60035460ff16156118ef5760405162461bcd60e51b81526004016104f9906127ad565b6001600460008282546119029190612a02565b90915550506004546006546001600160a01b031633146119345760405162461bcd60e51b81526004016104f990612a15565b6000848152600a60205260409020546001600160a01b03168061198f5760405162461bcd60e51b8152602060048201526013602482015272155b9adb9bdddb881b1a5d99481d1a58dad95d606a1b60448201526064016104f9565b6001600160a01b03848116600090815260096020908152604080832080546001600160a01b0319169486169485179055928252600b9052206119d1908561225f565b604080516001600160a01b038681168252602082018690528316818301526001606082015290517f7aaa2d826ef55c1cbb66bd84ac4b232ee95588980b3800ff76f23e286a8bcd359181900360800190a1506004548114611a445760405162461bcd60e51b81526004016104f990612a5c565b50505050565b6001600160a01b0381166000908152600b60205260409020606090610cc4908585611d48565b611a78611c7c565b60055461010090046001600160a01b031615801590611aa157506008546001600160a01b031615155b15611b295760085460055460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392611ae492610100909104909116906000906004016129b6565b6020604051808303816000875af1158015611b03573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b2791906129cf565b505b60058054610100600160a81b0319166101006001600160a01b0384169081029190911790915515801590611b6757506008546001600160a01b031615155b15611be55760085460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390611ba0908490600019906004016129b6565b6020604051808303816000875af1158015611bbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611be391906129cf565b505b6040516001600160a01b03821681527f9985022676a73860c32a3b91ea7a7dfe2d5e87c148f50eb519d8b0f33ab7f8b99060200161078e565b60055460ff1615611c675760405162461bcd60e51b8152602060048201526013602482015272105b1c9958591e481a5b9a5d1a585b1a5e9959606a1b60448201526064016104f9565b6005805460ff19166001908117909155600455565b6000546001600160a01b03163314611cee5760405162461bcd60e51b815260206004820152602f60248201527f4f6e6c792074686520636f6e7472616374206f776e6572206d6179207065726660448201526e37b936903a3434b99030b1ba34b7b760891b60648201526084016104f9565b565b610c9983846001600160a01b031663a9059cbb8585604051602401611d169291906129b6565b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506122b1565b60606000611d568385612a02565b8554909150811115611d66575083545b838111611d83575050604080516000815260208101909152610cc7565b6000611d8f8583612aa9565b905060008167ffffffffffffffff811115611dac57611dac612a93565b604051908082528060200260200182016040528015611dd5578160200160208202803683370190505b50905060005b82811015611e585787611dee8883612a02565b81548110611dfe57611dfe612abc565b9060005260206000200160009054906101000a90046001600160a01b0316828281518110611e2e57611e2e612abc565b6001600160a01b039092166020928302919091019091015280611e5081612ad2565b915050611ddb565b509695505050505050565b60075460405163605f5aa960e01b81526001600160a01b039091169063605f5aa990611e9590339089906004016129b6565b600060405180830381600087803b158015611eaf57600080fd5b505af1158015611ec3573d6000803e3d6000fd5b5060009250505060ff821615611f67576005546008546040516349d8615760e11b81526001600160a01b036101009093048316926393b0c2ae92611f1d928d928d928d928d928d928d929116906000908d90600401612dde565b6020604051808303816000875af1158015611f3c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f609190612e3a565b9050611ff4565b6005546008546040516214e17b60e41b81526001600160a01b0361010090930483169263014e17b092611fae928d928d928d928d928d928d92911690600090600401612e57565b6020604051808303816000875af1158015611fcd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ff19190612e3a565b90505b6001600160a01b038116600090815260096020908152604080832080546001600160a01b031916339081179091558352600b9091529020612035908261225f565b604080516001600160a01b03831681526020810188905233818301526000606082015290517f7aaa2d826ef55c1cbb66bd84ac4b232ee95588980b3800ff76f23e286a8bcd359181900360800190a15050505050505050565b815460009081036120a157506000612100565b6001600160a01b0382166000908152600184016020526040902054801515806120fc5750826001600160a01b0316846000016000815481106120e5576120e5612abc565b6000918252602090912001546001600160a01b0316145b9150505b92915050565b612110828261208e565b6121525760405162461bcd60e51b815260206004820152601360248201527222b632b6b2b73a103737ba1034b71039b2ba1760691b60448201526064016104f9565b6001600160a01b038116600090815260018084016020526040822054845490929161217c91612aa9565b905080821461220857600084600001828154811061219c5761219c612abc565b60009182526020909120015485546001600160a01b03909116915081908690859081106121cb576121cb612abc565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815260018601909152604090208290555b835484908061221957612219612ea8565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b0394909416815260019490940190925250506040812055565b612269828261208e565b610b4c5781546001600160a01b038216600081815260018086016020908152604083208590559084018655858252902090910180546001600160a01b03191690911790555050565b60006122c66001600160a01b03841683612314565b905080516000141580156122eb5750808060200190518101906122e991906129cf565b155b15610c9957604051635274afe760e01b81526001600160a01b03841660048201526024016104f9565b6060610cc78383600084600080856001600160a01b0316848660405161233a9190612ebe565b60006040518083038185875af1925050503d8060008114612377576040519150601f19603f3d011682016040523d82523d6000602084013e61237c565b606091505b509150915061238c868383612396565b9695505050505050565b6060826123ab576123a6826123f2565b610cc7565b81511580156123c257506001600160a01b0384163b155b156123eb57604051639996b31560e01b81526001600160a01b03851660048201526024016104f9565b5080610cc7565b8051156124025780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60006020828403121561242d57600080fd5b813567ffffffffffffffff81111561244457600080fd5b82016101408185031215610cc757600080fd5b6001600160a01b0381168114610b2d57600080fd5b803561247781612457565b919050565b60006020828403121561248e57600080fd5b8135610cc781612457565b600080600080600060a086880312156124b157600080fd5b85356124bc81612457565b945060208601356124cc81612457565b935060408601356124dc81612457565b925060608601356124ec81612457565b915060808601356124fc81612457565b809150509295509295909350565b8015158114610b2d57600080fd5b60006020828403121561252a57600080fd5b8135610cc78161250a565b6000806040838503121561254857600080fd5b823561255381612457565b946020939093013593505050565b6000806040838503121561257457600080fd5b50508035926020909101359150565b60006020828403121561259557600080fd5b5035919050565b6000806000606084860312156125b157600080fd5b833592506020840135915060408401356125ca81612457565b809150509250925092565b6020808252825182820181905260009190848201906040850190845b818110156126165783516001600160a01b0316835292840192918401916001016125f1565b50909695505050505050565b60008083601f84011261263457600080fd5b50813567ffffffffffffffff81111561264c57600080fd5b6020830191508360208260051b850101111561266757600080fd5b9250929050565b60008060008060008060a0878903121561268757600080fd5b863567ffffffffffffffff81111561269e57600080fd5b6126aa89828a01612622565b90975095505060208701359350604087013592506060870135915060808701356126d381612457565b809150509295509295509295565b803560ff8116811461247757600080fd5b600080600080600080600060c0888a03121561270d57600080fd5b873567ffffffffffffffff81111561272457600080fd5b6127308a828b01612622565b909850965050602088013594506040880135935060608801359250608088013561275981612457565b915061276760a089016126e1565b905092959891949750929550565b60008060006060848603121561278a57600080fd5b83359250602084013561279c81612457565b929592945050506040919091013590565b6020808252603c908201527f5468697320616374696f6e2063616e6e6f7420626520706572666f726d65642060408201527f7768696c652074686520636f6e74726163742069732070617573656400000000606082015260800190565b6000808335601e1984360301811261282157600080fd5b830160208101925035905067ffffffffffffffff81111561284157600080fd5b80360382131561266757600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b803561ffff8116811461247757600080fd5b8035600281900b811461247757600080fd5b6020815260006128ad838461280a565b6101408060208601526128c561016086018385612850565b92506128d360208701612879565b61ffff8116604087015291506128eb60408701612879565b61ffff8116606087015291506129036060870161288b565b9150612914608086018360020b9052565b612920608087016126e1565b60ff811660a0870152915060a086013560c086015260c086013560e0860152610100915060e08601358286015261295882870161246c565b9150610120612971818701846001600160a01b03169052565b61297c81880161246c565b925050612993818601836001600160a01b03169052565b5090949350505050565b6000602082840312156129af57600080fd5b5051919050565b6001600160a01b03929092168252602082015260400190565b6000602082840312156129e157600080fd5b8151610cc78161250a565b634e487b7160e01b600052601160045260246000fd5b80820180821115612100576121006129ec565b60208082526027908201527f4f6e6c792063616c6c61626c652066726f6d204c69766554726164696e67507260408201526637b1b2b9b9b7b960c91b606082015260800190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b600052604160045260246000fd5b81810381811115612100576121006129ec565b634e487b7160e01b600052603260045260246000fd5b600060018201612ae457612ae46129ec565b5060010190565b803562ffffff8116811461247757600080fd5b6000808335601e19843603018112612b1557600080fd5b830160208101925035905067ffffffffffffffff811115612b3557600080fd5b8060051b360382131561266757600080fd5b81835260006001600160fb1b03831115612b6057600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b85811015612bd95761ffff612b9d83612879565b16875260ff612bad8484016126e1565b16838801526040612bbf81840161288b565b60020b908801526060968701969190910190600101612b89565b509495945050505050565b81835260006020808501808196508560051b810191508460005b87811015612c6c5782840389528135601e19883603018112612c1f57600080fd5b8701858101903567ffffffffffffffff811115612c3b57600080fd5b606081023603821315612c4d57600080fd5b612c58868284612b79565b9a87019a9550505090840190600101612bfe565b5091979650505050505050565b81835260006020808501808196508560051b81019150846000805b88811015612dd0578385038a52823561015e19893603018112612cb5578283fd5b880180358652610160612cc9888301612879565b61ffff16888801526040612cde838201612879565b61ffff1690880152606082810135908801526080612cfd8184016126e1565b60ff169088015260a0612d1183820161288b565b612d1f828a018260020b9052565b505060c0612d2e818401612aeb565b62ffffff169088015260e0612d4583820184612afe565b83838b0152612d57848b018284612b47565b9350505050610100612d6b81840184612afe565b898403838b0152612d7d848284612b47565b9350505050610120612d908184016126e1565b60ff1690880152610140612da683820184612afe565b9350888303828a0152612dba838583612be4565b9d8a019d98505050938701935050600101612c94565b509298975050505050505050565b6000610100808352612df38184018c8e612c79565b602084019a909a525050604081019690965260608601949094526001600160a01b039283166080860152911660a0840152151560c083015260ff1660e09091015292915050565b600060208284031215612e4c57600080fd5b8151610cc781612457565b60e081526000612e6b60e083018a8c612c79565b602083019890985250604081019590955260608501939093526001600160a01b0391821660808501521660a0830152151560c09091015292915050565b634e487b7160e01b600052603160045260246000fd5b6000825160005b81811015612edf5760208186018101518583015201612ec5565b50600092019182525091905056fea2646970667358221220aca66c3927b3c5cb53998e79eeeede24514f342afa95697e7d1d87c00a77bde064736f6c63430008140033
Loading...
Loading
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.