Overview
S Balance
S Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 5 from a total of 5 transactions
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
12000929 | 4 days ago | Contract Creation | 0 S |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
GameFactory
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Business-Source-License-1.1 // https://pob.fun/license // From 2028-02-18, this code is licensed under the MIT License. pragma solidity ^0.8.26; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IEntropyConsumer } from "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol"; import { IEntropy } from "@pythnetwork/entropy-sdk-solidity/IEntropy.sol"; interface IBurnable { function burn(uint256 amount) external; } /*////////////////////////////////////////////////////////////// CUSTOM ERRORS //////////////////////////////////////////////////////////////*/ error NotManager(); error NotAdmin(); error ZeroAdminAddress(); error ZeroManagerAddress(); error ZeroEntropyAddress(); error PrizeMustBeGreaterThanZero(); error NotAuthorizedToCreateGame(); error NotAuthorizedGame(); error GameAlreadyEnded(); error PastEndTime(); error BeforeEndTime(); error ZeroAmount(); error RandomNotReady(); error PrizeAlreadyClaimed(); error AlreadyFulfilled(); error NoWinnerFound(); error FeeTooLow(); error NotFactory(); /** * @title GameFactory * @notice Deploys and manages Game contracts. */ contract GameFactory is ReentrancyGuard { using SafeERC20 for IERC20; /*////////////////////////////////////////////////////////////// STATE //////////////////////////////////////////////////////////////*/ /// @notice The administrator address with elevated privileges. address public admin; /// @notice Mapping of addresses allowed to create games (if restricted). mapping(address => bool) public managers; /// @notice The Entropy contract address used for randomness. address public entropyAddress; /// @notice Burn address for tokens. address constant ZERO_ADDRESS = address(0); address constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD; /// @notice Mapping to track authorized game addresses. mapping(address => bool) private authorizedGames; /// @notice Maps a game address to its index in `allGames`. mapping(address => uint256) public gameIndex; /// @notice Struct to hold game details for external viewing. struct GameInfo { address gameAddress; uint256 numParticipants; uint256 totalBurned; address token; uint256 deadline; uint256 prizeAmount; uint256 finalPrize; uint256 tokenDecimals; string tokenSymbol; string tag; } /// @notice Array holding information about all created games. GameInfo[] public allGames; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** * @notice Emitted when a new game is created. * @param gameAddress The address of the newly created game. * @param creator The address that initiated the creation. * @param tokenAddress The address of the ERC20 token used for the game prize. */ event GameCreated( address indexed gameAddress, address indexed creator, address tokenAddress ); /** * @notice Emitted when game information is updated. * @param gameAddress The address of the game being updated. * @param numParticipants The current number of participants in the game. * @param totalBurned The total amount of tokens burned for the game. */ event GameInfoUpdated( address indexed gameAddress, uint256 numParticipants, uint256 totalBurned ); /** * @notice Emitted when tokens are burned via `depositIntoGame`. * @param sender The address that sent tokens to be burned. * @param amount The actual amount of tokens burned (accounting for taxes). */ event TokensBurned(address indexed sender, uint256 amount); /** * @dev Emitted when tokens are burned using one of the burn methods. * @param token The address of the ERC20 token that was attempted to be burned. * @param attemptedAmount The total amount of tokens the function attempted to burn. * @param actualBurned The actual amount of tokens removed from the contract’s balance after the burn operation. * @param method A string identifier indicating the burn method used: * - "ZeroAddressTransfer" if tokens were sent to the zero address, * - "BurnFunction" if the token’s burn() function was successfully called, * - "DeadAddressTransfer" if tokens were sent to the dead address. */ event TokenBurnMethodUsed( address indexed token, uint256 attemptedAmount, uint256 actualBurned, string method ); event BurnMethodSkipped(address indexed token, string reason); /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ /** * @dev Restricts function access to only the admin. */ modifier onlyAdmin() { if (msg.sender != admin) revert NotAdmin(); _; } /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ /** * @notice Sets the admin and initializes the default Entropy contract. * @param _admin The address of the admin. */ constructor(address _admin) { if (_admin == address(0)) revert ZeroAdminAddress(); admin = _admin; // Default Entropy address (can be updated via updateEntropyAddress). entropyAddress = 0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320; // } /*////////////////////////////////////////////////////////////// INTERNAL LOGIC //////////////////////////////////////////////////////////////*/ function _supportsBurnMethod(address token) internal view returns (bool) { (bool success, ) = token.staticcall(abi.encodeWithSelector(IBurnable.burn.selector, 1)); return success; } function _attemptTokenBurn(IERC20 token, uint256 amount) internal returns (uint256) { uint256 balanceBefore = token.balanceOf(address(this)); uint256 burnedAmount; // 1. Check if the burn() method exists before calling it if (_supportsBurnMethod(address(token))) { try IBurnable(address(token)).burn(amount) { burnedAmount = balanceBefore - token.balanceOf(address(this)); emit TokenBurnMethodUsed(address(token), amount, burnedAmount, "BurnFunction"); return burnedAmount; } catch { emit BurnMethodSkipped(address(token), "BurnFunction reverted"); } } else { emit BurnMethodSkipped(address(token), "BurnFunction not supported"); } // 2. Try sending to the zero address using a low-level call { (bool success, bytes memory data) = address(token).call( abi.encodeWithSelector(token.transfer.selector, ZERO_ADDRESS, amount) ); bool transferred = success && (data.length == 0 || abi.decode(data, (bool))); if (transferred) { burnedAmount = balanceBefore - token.balanceOf(address(this)); emit TokenBurnMethodUsed(address(token), amount, burnedAmount, "ZeroAddressTransfer"); return burnedAmount; } } // 3. If both fail, use SafeERC20 to transfer to DEAD_ADDRESS token.safeTransfer(DEAD_ADDRESS, amount); burnedAmount = balanceBefore - token.balanceOf(address(this)); emit TokenBurnMethodUsed(address(token), amount, burnedAmount, "DeadAddressTransfer"); return burnedAmount; } /*////////////////////////////////////////////////////////////// EXTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Deposits (and burns) tokens into an existing game. * @dev Pulls tokens from the caller to this factory, then attempts to burn them. * @param gameAddress The address of the game to deposit into. * @param amount The amount of tokens the user wishes to deposit (burn). */ function depositIntoGame(address gameAddress, uint256 amount) external nonReentrant { if (!authorizedGames[gameAddress]) revert NotAuthorizedGame(); uint256 idx = gameIndex[gameAddress]; GameInfo storage info = allGames[idx]; if (block.timestamp >= info.deadline) revert PastEndTime(); Game game = Game(gameAddress); if (game.gameEnded()) revert GameAlreadyEnded(); if (amount == 0) revert ZeroAmount(); IERC20 token = IERC20(info.token); // 1. Measure factory's balance before pull. uint256 factoryBalanceBefore = token.balanceOf(address(this)); // 2. Pull tokens from user -> factory (SafeERC20 reverts on fail). token.safeTransferFrom(msg.sender, address(this), amount); // 3. Measure factory balance after pull. uint256 factoryBalanceAfterUserDeposit = token.balanceOf(address(this)); // The net tokens *received* from user (accounting for any first tax). uint256 netDeposit = factoryBalanceAfterUserDeposit - factoryBalanceBefore; // 4. Attempt to burn `netDeposit` from the factory using multiple methods. uint256 actualBurned = _attemptTokenBurn(token, netDeposit); emit TokensBurned(msg.sender, actualBurned); // 5. Notify the Game contract of the actual amount burned. game.recordDeposit(msg.sender, actualBurned); } /** * @notice Creates a new game and transfers the prize directly into the new contract. * @dev The caller must have approved this factory to spend `_prizeAmount`. * @param _token The ERC20 token address to be used for prizes. * @param _endTimestamp The game end timestamp. * @param _prizeAmount The amount of tokens to be transferred as the initial prize. * @param _tag An arbitrary tag (metadata) for the game. * @return gameAddress The address of the newly created game contract. */ function createGame( address _token, uint64 _endTimestamp, uint256 _prizeAmount, string calldata _tag ) external nonReentrant returns (address gameAddress) { // If only managers can create games, ensure caller is admin or manager. if (msg.sender != admin && !managers[msg.sender]) revert NotAuthorizedToCreateGame(); // Ensure the end time is in the future. if (_endTimestamp <= block.timestamp) revert PastEndTime(); // ensure prize is non 0 if (_prizeAmount == 0) revert PrizeMustBeGreaterThanZero(); // 1. Deploy a fresh Game contract. Game newGame = new Game( _token, _endTimestamp, entropyAddress, msg.sender, _tag ); IERC20 token = IERC20(_token); // 2. Measure the new Game contract's balance before the transfer. uint256 gameBalanceBefore = token.balanceOf(address(newGame)); // 3. Transfer `_prizeAmount` directly from the caller to the new Game contract (SafeERC20). token.safeTransferFrom(msg.sender, address(newGame), _prizeAmount); // 4. Measure how many tokens actually arrived (accounting for any token tax/deflation). uint256 gameBalanceAfter = token.balanceOf(address(newGame)); uint256 netPrize = gameBalanceAfter - gameBalanceBefore; // Mark this new game as authorized. authorizedGames[address(newGame)] = true; // 5. Store the new game info. allGames.push( GameInfo({ gameAddress: address(newGame), numParticipants: 0, totalBurned: 0, token: _token, deadline: _endTimestamp, prizeAmount: netPrize, finalPrize: 0, tokenSymbol: IERC20Metadata(_token).symbol(), tokenDecimals: IERC20Metadata(_token).decimals(), tag: _tag }) ); // 6. Track the index for future reference. uint256 idx = allGames.length - 1; gameIndex[address(newGame)] = idx; // 7. Emit an event. emit GameCreated(address(newGame), msg.sender, _token); // 8. Return the new game address. return address(newGame); } /** * @notice Updates the address of the Entropy contract. * @param _newEntropyAddress The new Entropy contract address. */ function updateEntropyAddress(address _newEntropyAddress) external onlyAdmin { if (_newEntropyAddress == address(0)) revert ZeroEntropyAddress(); entropyAddress = _newEntropyAddress; } /** * @notice Updates the admin address. * @param _newAdmin The new admin address. */ function updateAdmin(address _newAdmin) external onlyAdmin { if (_newAdmin == address(0)) revert ZeroAdminAddress(); admin = _newAdmin; } /** * @notice Adds a new manager with game-creation privileges. * @param managerAddress The address to be granted manager privileges. */ function addManager(address managerAddress) external onlyAdmin { if (managerAddress == address(0)) revert ZeroManagerAddress(); managers[managerAddress] = true; } /** * @notice Removes manager privileges from an address. * @param managerAddress The address to remove from manager privileges. */ function removeManager(address managerAddress) external onlyAdmin { if (managerAddress == address(0)) revert ZeroManagerAddress(); managers[managerAddress] = false; } /** * @notice Returns the full list of all created games. * @return An array of `GameInfo` structs. */ function getAllGames() external view returns (GameInfo[] memory) { return allGames; } /** * @notice Updates the final prize for the caller's game. * @dev Only callable by an authorized game contract. * @param _finalPrize The final prize amount (for record-keeping). */ function updateFinalPrize(uint256 _finalPrize) external { if (!authorizedGames[msg.sender]) revert NotAuthorizedGame(); uint256 idx = gameIndex[msg.sender]; GameInfo storage info = allGames[idx]; info.finalPrize = _finalPrize; } /** * @notice Updates game info such as participant count or total burned amount. * @dev Only callable by an authorized game contract. * @param numParticipants The updated number of participants. * @param totalBurned The updated total burned amount. */ function updateGameInfo( uint256 numParticipants, uint256 totalBurned ) external { if (!authorizedGames[msg.sender]) revert NotAuthorizedGame(); uint256 idx = gameIndex[msg.sender]; GameInfo storage info = allGames[idx]; info.numParticipants = numParticipants; info.totalBurned = totalBurned; emit GameInfoUpdated(msg.sender, numParticipants, totalBurned); } } /** * @title Game * @notice A single-round lottery-style game using token burns for entries. */ contract Game is IEntropyConsumer, ReentrancyGuard { using SafeERC20 for IERC20; /*////////////////////////////////////////////////////////////// STATE //////////////////////////////////////////////////////////////*/ /// @notice The ERC20 token used in this game. IERC20 public immutable token; /// @notice The Entropy interface for randomness. IEntropy public immutable entropy; /// @notice The factory address that deployed this game. address public immutable factory; /// @notice The creator address of this game. address public immutable gameCreator; /// @notice The timestamp after which the game can be ended. uint64 public immutable endTimestamp; /// @notice The number of decimals for the token (cached from the token contract). uint8 public immutable tokenDecimals; /// @notice The symbol for the token (cached from the token contract). string public tokenSymbol; /// @notice Indicates if the game has ended. bool public gameEnded; /// @notice The total amount of tokens burned by all participants. uint256 public totalBurned; /// @notice The total number of unique participants in the game. uint256 public numParticipants; /// @notice True if the random number for this game was already fulfilled. bool public randomFulfilled; /// @notice The winner address selected by randomness. address public winner; /// @notice True if the winner (or creator if no participants) has claimed the prize. bool public winnerClaimed; /// @notice Arbitrary metadata tag for the game. string public tag; /// @notice The final prize snapshot, saved at claim time for historical reference. uint256 public finalPrize; /// @notice Struct for tracking user entry ranges in the burn-based index. struct Range { address user; uint256 end; } Range[] public ranges; /// @notice Readable structure for front-end usage (returned by `getGameStatus`). struct GameStatusView { bool isEnded; bool fulfilled; uint256 timeLeft; address[] participants; uint256[] shares; address erc20Token; string symbol; uint8 decimals; uint256 burns; uint256 totalPrize; address prizeWinner; bool prizeWasClaimed; uint256 finalPrize; string tag; } /// @notice Mapping from user address to total tokens burned by that user in this game. mapping(address => uint256) public userBurns; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /** * @notice Emitted when a user burns tokens for the game. * @param user The address of the participant. * @param amount The amount of tokens burned. */ event Burned(address indexed user, uint256 amount); /** * @notice Emitted when the game ends and a randomness request is triggered. * @param requestId The sequence number for the Entropy request. */ event GameEnded(uint256 requestId); /** * @notice Emitted when a winner is selected by randomness. * @param winner The address of the selected winner. */ event WinnerSelected(address indexed winner); /** * @notice Emitted after the randomness callback is processed. * @param sequenceNumber The sequence number of the Entropy request. * @param provider The address of the Entropy provider. */ event EntropyCalledBack(uint64 sequenceNumber, address indexed provider); /** * @notice Emitted when the prize is claimed by the winner (or returned to creator if no participants). * @param winner The address claiming the prize. * @param amount The amount of tokens claimed. */ event PrizeClaimed(address indexed winner, uint256 amount); /** * @notice Emitted if there are no participants, and the prize is returned to the game creator. * @param winner The address returning the prize (the game creator). * @param amount The amount of tokens returned. */ event PrizeReturned(address indexed winner, uint256 amount); /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ /** * @notice Sets up a new Game contract. * @param _token The ERC20 token used by this game. * @param _endTimestamp The timestamp after which the game can be ended. * @param _entropyAddress The address of the Entropy contract. * @param _gameCreator The address that is creating this game. * @param _tag Arbitrary metadata for the game. */ constructor( address _token, uint64 _endTimestamp, address _entropyAddress, address _gameCreator, string memory _tag ) { if (_endTimestamp <= block.timestamp) revert PastEndTime(); token = IERC20(_token); entropy = IEntropy(_entropyAddress); factory = msg.sender; gameCreator = _gameCreator; endTimestamp = _endTimestamp; tokenSymbol = IERC20Metadata(_token).symbol(); tokenDecimals = IERC20Metadata(_token).decimals(); tag = _tag; } /*////////////////////////////////////////////////////////////// EXTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Records a user deposit (burn) into this game. * @dev Only callable by the factory. * @param user The address depositing tokens. * @param burnAmount The amount of tokens burned. */ function recordDeposit(address user, uint256 burnAmount) external nonReentrant { if (msg.sender != factory) revert NotFactory(); // If this is the user's first deposit, increment participant count. if (userBurns[user] == 0) { numParticipants++; } userBurns[user] += burnAmount; totalBurned += burnAmount; // Update the ranges array for random selection. uint256 endSlot = totalBurned - 1; if (ranges.length != 0 && ranges[ranges.length - 1].user == user) { ranges[ranges.length - 1].end += burnAmount; } else { ranges.push(Range({ user: user, end: endSlot })); } emit Burned(user, burnAmount); // Notify the factory to keep track of participants and burned amount. GameFactory(factory).updateGameInfo(numParticipants, totalBurned); } /** * @notice Ends the game after `endTimestamp` and requests random from Pyth Entropy. * @dev After calling, it will initiate the randomness callback workflow. * @param userRandomNumber An optional user-supplied seed. */ function endGame(bytes32 userRandomNumber) external payable nonReentrant { if (gameEnded) revert GameAlreadyEnded(); if (block.timestamp < endTimestamp) revert BeforeEndTime(); gameEnded = true; address entropyProvider = entropy.getDefaultProvider(); uint256 fee = entropy.getFee(entropyProvider); if(msg.value < fee) revert FeeTooLow(); if (msg.value > fee) { payable(msg.sender).transfer(msg.value - fee); } // Request randomness with callback. uint64 sequenceNumber = entropy.requestWithCallback{ value: fee }( entropyProvider, userRandomNumber ); emit GameEnded(sequenceNumber); } /** * @notice Gets the fee required for requesting randomness from the default provider. * @return fee The required fee in wei. */ function getFee() external view returns (uint256 fee) { address entropyProvider = entropy.getDefaultProvider(); fee = entropy.getFee(entropyProvider); } /** * @notice Allows the winner (or game creator if no participants) to claim the prize. * @dev Must be called after the random number is fulfilled. */ function claimPrize() external nonReentrant { uint256 prizeAmount = token.balanceOf(address(this)); if (!gameEnded) revert GameAlreadyEnded(); if (!randomFulfilled) revert RandomNotReady(); if (winnerClaimed) revert PrizeAlreadyClaimed(); finalPrize = prizeAmount; // Update the final prize in the factory for record-keeping. GameFactory(factory).updateFinalPrize(prizeAmount); winnerClaimed = true; uint256 payout = prizeAmount; prizeAmount = 0; // If there were no burns, no winner is selected => prize goes back to gameCreator. if (totalBurned == 0) { token.safeTransfer(gameCreator, payout); emit PrizeReturned(gameCreator, payout); } else { token.safeTransfer(winner, payout); emit PrizeClaimed(winner, payout); } } /*////////////////////////////////////////////////////////////// ENTROPY CONSUMER (RNG) LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Internal callback executed once the randomness is fulfilled. * @param sequenceNumber The Entropy request sequence number. * @param provider The address of the Entropy provider. * @param randomNumber The random number returned from Entropy. */ function entropyCallback( uint64 sequenceNumber, address provider, bytes32 randomNumber ) internal override { if (!gameEnded) revert GameAlreadyEnded(); if (randomFulfilled) revert AlreadyFulfilled(); randomFulfilled = true; uint256 rangeSize = totalBurned; // If there were no participants, winner stays address(0). if (rangeSize == 0) { winner = address(0); } else { // Randomly select a slot in [0, rangeSize - 1]. uint256 randomNormalized = uint256(randomNumber) % rangeSize; winner = findWinnerByBinarySearch(randomNormalized); } emit WinnerSelected(winner); emit EntropyCalledBack(sequenceNumber, provider); } /** * @notice Returns the address of the Entropy contract to the `IEntropyConsumer` interface. */ function getEntropy() internal view override returns (address) { return address(entropy); } /*////////////////////////////////////////////////////////////// INTERNAL / VIEW FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @notice Finds the winner by performing a binary search over the `ranges` array. * @param winningSlot The random slot index (0-based). * @return The address of the winning participant. */ function findWinnerByBinarySearch(uint256 winningSlot) internal view returns (address) { uint256 left = 0; uint256 right = ranges.length - 1; while (left <= right) { uint256 mid = (left + right) >> 1; // mid = (left + right) / 2 Range memory midRange = ranges[mid]; if (winningSlot <= midRange.end) { return midRange.user; } else { left = mid + 1; } } revert NoWinnerFound(); } /*////////////////////////////////////////////////////////////// PUBLIC VIEW //////////////////////////////////////////////////////////////*/ /** * @notice Returns the number of ranges used in random selection. * @return The length of the `ranges` array. */ function getRangeCount() external view returns (uint256) { return ranges.length; } /** * @notice Returns the user and end index for a specific range. * @param index The index of the range in the `ranges` array. * @return user The address of the participant. * @return endSlot The last burn slot index belonging to this user. */ function getRange(uint256 index) external view returns (address user, uint256 endSlot) { Range memory r = ranges[index]; return (r.user, r.end); } /** * @notice Returns a view struct containing most game data in a single call. * @return status A `GameStatusView` struct with relevant game information. */ function getGameStatus() external view returns (GameStatusView memory status) { status.isEnded = gameEnded; status.fulfilled = randomFulfilled; status.timeLeft = block.timestamp < endTimestamp ? endTimestamp - uint64(block.timestamp) : 0; status.erc20Token = address(token); status.symbol = tokenSymbol; status.decimals = tokenDecimals; status.burns = totalBurned; status.totalPrize = token.balanceOf(address(this)); status.prizeWinner = winner; status.prizeWasClaimed = winnerClaimed; status.finalPrize = finalPrize; status.tag = tag; uint256 rangeCount = ranges.length; if (rangeCount == 0) { // No participants => empty arrays. status.participants = new address[](0); status.shares = new uint256[](0); return status; } // Deduplicate participants and sum their shares in memory. address[] memory tempParticipants = new address[](rangeCount); uint256[] memory tempShares = new uint256[](rangeCount); uint256 uniqueCount; for (uint256 i = 0; i < rangeCount; i++) { Range memory currentRange = ranges[i]; uint256 rangeStart = (i == 0) ? 0 : (ranges[i - 1].end + 1); uint256 rangeEnd = currentRange.end; uint256 rangeSlotCount = rangeEnd - rangeStart + 1; // Check if user is already in temp array. bool found; for (uint256 j = 0; j < uniqueCount; j++) { if (tempParticipants[j] == currentRange.user) { tempShares[j] += rangeSlotCount; found = true; break; } } // If not found, add a new entry. if (!found) { tempParticipants[uniqueCount] = currentRange.user; tempShares[uniqueCount] = rangeSlotCount; uniqueCount++; } } // Create final arrays with exact sizes. address[] memory finalParticipants = new address[](uniqueCount); uint256[] memory finalShares = new uint256[](uniqueCount); for (uint256 i = 0; i < uniqueCount; i++) { finalParticipants[i] = tempParticipants[i]; finalShares[i] = tempShares[i]; } // Populate the return struct. status.participants = finalParticipants; status.shares = finalShares; return status; } }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; import "./EntropyEvents.sol"; interface IEntropy is EntropyEvents { // Register msg.sender as a randomness provider. The arguments are the provider's configuration parameters // and initial commitment. Re-registering the same provider rotates the provider's commitment (and updates // the feeInWei). // // chainLength is the number of values in the hash chain *including* the commitment, that is, chainLength >= 1. function register( uint128 feeInWei, bytes32 commitment, bytes calldata commitmentMetadata, uint64 chainLength, bytes calldata uri ) external; // Withdraw a portion of the accumulated fees for the provider msg.sender. // Calling this function will transfer `amount` wei to the caller (provided that they have accrued a sufficient // balance of fees in the contract). function withdraw(uint128 amount) external; // Withdraw a portion of the accumulated fees for provider. The msg.sender must be the fee manager for this provider. // Calling this function will transfer `amount` wei to the caller (provided that they have accrued a sufficient // balance of fees in the contract). function withdrawAsFeeManager(address provider, uint128 amount) external; // As a user, request a random number from `provider`. Prior to calling this method, the user should // generate a random number x and keep it secret. The user should then compute hash(x) and pass that // as the userCommitment argument. (You may call the constructUserCommitment method to compute the hash.) // // This method returns a sequence number. The user should pass this sequence number to // their chosen provider (the exact method for doing so will depend on the provider) to retrieve the provider's // number. The user should then call fulfillRequest to construct the final random number. // // This method will revert unless the caller provides a sufficient fee (at least getFee(provider)) as msg.value. // Note that excess value is *not* refunded to the caller. function request( address provider, bytes32 userCommitment, bool useBlockHash ) external payable returns (uint64 assignedSequenceNumber); // Request a random number. The method expects the provider address and a secret random number // in the arguments. It returns a sequence number. // // The address calling this function should be a contract that inherits from the IEntropyConsumer interface. // The `entropyCallback` method on that interface will receive a callback with the generated random number. // // This method will revert unless the caller provides a sufficient fee (at least getFee(provider)) as msg.value. // Note that excess value is *not* refunded to the caller. function requestWithCallback( address provider, bytes32 userRandomNumber ) external payable returns (uint64 assignedSequenceNumber); // Fulfill a request for a random number. This method validates the provided userRandomness and provider's proof // against the corresponding commitments in the in-flight request. If both values are validated, this function returns // the corresponding random number. // // Note that this function can only be called once per in-flight request. Calling this function deletes the stored // request information (so that the contract doesn't use a linear amount of storage in the number of requests). // If you need to use the returned random number more than once, you are responsible for storing it. function reveal( address provider, uint64 sequenceNumber, bytes32 userRevelation, bytes32 providerRevelation ) external returns (bytes32 randomNumber); // Fulfill a request for a random number. This method validates the provided userRandomness // and provider's revelation against the corresponding commitment in the in-flight request. If both values are validated // and the requestor address is a contract address, this function calls the requester's entropyCallback method with the // sequence number, provider address and the random number as arguments. Else if the requestor is an EOA, it won't call it. // // Note that this function can only be called once per in-flight request. Calling this function deletes the stored // request information (so that the contract doesn't use a linear amount of storage in the number of requests). // If you need to use the returned random number more than once, you are responsible for storing it. // // Anyone can call this method to fulfill a request, but the callback will only be made to the original requester. function revealWithCallback( address provider, uint64 sequenceNumber, bytes32 userRandomNumber, bytes32 providerRevelation ) external; function getProviderInfo( address provider ) external view returns (EntropyStructs.ProviderInfo memory info); function getDefaultProvider() external view returns (address provider); function getRequest( address provider, uint64 sequenceNumber ) external view returns (EntropyStructs.Request memory req); function getFee(address provider) external view returns (uint128 feeAmount); function getAccruedPythFees() external view returns (uint128 accruedPythFeesInWei); function setProviderFee(uint128 newFeeInWei) external; function setProviderFeeAsFeeManager( address provider, uint128 newFeeInWei ) external; function setProviderUri(bytes calldata newUri) external; // Set manager as the fee manager for the provider msg.sender. // After calling this function, manager will be able to set the provider's fees and withdraw them. // Only one address can be the fee manager for a provider at a time -- calling this function again with a new value // will override the previous value. Call this function with the all-zero address to disable the fee manager role. function setFeeManager(address manager) external; function constructUserCommitment( bytes32 userRandomness ) external pure returns (bytes32 userCommitment); function combineRandomValues( bytes32 userRandomness, bytes32 providerRandomness, bytes32 blockHash ) external pure returns (bytes32 combinedRandomness); }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; abstract contract IEntropyConsumer { // This method is called by Entropy to provide the random number to the consumer. // It asserts that the msg.sender is the Entropy contract. It is not meant to be // override by the consumer. function _entropyCallback( uint64 sequence, address provider, bytes32 randomNumber ) external { address entropy = getEntropy(); require(entropy != address(0), "Entropy address not set"); require(msg.sender == entropy, "Only Entropy can call this function"); entropyCallback(sequence, provider, randomNumber); } // getEntropy returns Entropy contract address. The method is being used to check that the // callback is indeed from Entropy contract. The consumer is expected to implement this method. // Entropy address can be found here - https://docs.pyth.network/entropy/contract-addresses function getEntropy() internal view virtual returns (address); // This method is expected to be implemented by the consumer to handle the random number. // It will be called by _entropyCallback after _entropyCallback ensures that the call is // indeed from Entropy contract. function entropyCallback( uint64 sequence, address provider, bytes32 randomNumber ) internal virtual; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../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; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @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, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied 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. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @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 making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; import "./EntropyStructs.sol"; interface EntropyEvents { event Registered(EntropyStructs.ProviderInfo provider); event Requested(EntropyStructs.Request request); event RequestedWithCallback( address indexed provider, address indexed requestor, uint64 indexed sequenceNumber, bytes32 userRandomNumber, EntropyStructs.Request request ); event Revealed( EntropyStructs.Request request, bytes32 userRevelation, bytes32 providerRevelation, bytes32 blockHash, bytes32 randomNumber ); event RevealedWithCallback( EntropyStructs.Request request, bytes32 userRandomNumber, bytes32 providerRevelation, bytes32 randomNumber ); event ProviderFeeUpdated(address provider, uint128 oldFee, uint128 newFee); event ProviderUriUpdated(address provider, bytes oldUri, bytes newUri); event ProviderFeeManagerUpdated( address provider, address oldFeeManager, address newFeeManager ); event Withdrawal( address provider, address recipient, uint128 withdrawnAmount ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @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://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @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, it is bubbled up by this * function (like regular Solidity function calls). * * 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. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @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`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @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. */ 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]. */ 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 v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @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 amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` 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 amount ) external returns (bool); }
// SPDX-License-Identifier: Apache 2 pragma solidity ^0.8.0; contract EntropyStructs { struct ProviderInfo { uint128 feeInWei; uint128 accruedFeesInWei; // The commitment that the provider posted to the blockchain, and the sequence number // where they committed to this. This value is not advanced after the provider commits, // and instead is stored to help providers track where they are in the hash chain. bytes32 originalCommitment; uint64 originalCommitmentSequenceNumber; // Metadata for the current commitment. Providers may optionally use this field to help // manage rotations (i.e., to pick the sequence number from the correct hash chain). bytes commitmentMetadata; // Optional URI where clients can retrieve revelations for the provider. // Client SDKs can use this field to automatically determine how to retrieve random values for each provider. // TODO: specify the API that must be implemented at this URI bytes uri; // The first sequence number that is *not* included in the current commitment (i.e., an exclusive end index). // The contract maintains the invariant that sequenceNumber <= endSequenceNumber. // If sequenceNumber == endSequenceNumber, the provider must rotate their commitment to add additional random values. uint64 endSequenceNumber; // The sequence number that will be assigned to the next inbound user request. uint64 sequenceNumber; // The current commitment represents an index/value in the provider's hash chain. // These values are used to verify requests for future sequence numbers. Note that // currentCommitmentSequenceNumber < sequenceNumber. // // The currentCommitment advances forward through the provider's hash chain as values // are revealed on-chain. bytes32 currentCommitment; uint64 currentCommitmentSequenceNumber; // An address that is authorized to set / withdraw fees on behalf of this provider. address feeManager; } struct Request { // Storage slot 1 // address provider; uint64 sequenceNumber; // The number of hashes required to verify the provider revelation. uint32 numHashes; // Storage slot 2 // // The commitment is keccak256(userCommitment, providerCommitment). Storing the hash instead of both saves 20k gas by // eliminating 1 store. bytes32 commitment; // Storage slot 3 // // The number of the block where this request was created. // Note that we're using a uint64 such that we have an additional space for an address and other fields in // this storage slot. Although block.number returns a uint256, 64 bits should be plenty to index all of the // blocks ever generated. uint64 blockNumber; // The address that requested this random number. address requester; // If true, incorporate the blockhash of blockNumber into the generated random value. bool useBlockhash; // If true, the requester will be called back with the generated random value. bool isRequestWithCallback; // There are 2 remaining bytes of free space in this slot. } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "remappings": [] }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"GameAlreadyEnded","type":"error"},{"inputs":[],"name":"NotAdmin","type":"error"},{"inputs":[],"name":"NotAuthorizedGame","type":"error"},{"inputs":[],"name":"NotAuthorizedToCreateGame","type":"error"},{"inputs":[],"name":"PastEndTime","type":"error"},{"inputs":[],"name":"PrizeMustBeGreaterThanZero","type":"error"},{"inputs":[],"name":"ZeroAdminAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"inputs":[],"name":"ZeroEntropyAddress","type":"error"},{"inputs":[],"name":"ZeroManagerAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"string","name":"reason","type":"string"}],"name":"BurnMethodSkipped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gameAddress","type":"address"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"}],"name":"GameCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gameAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"numParticipants","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBurned","type":"uint256"}],"name":"GameInfoUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"attemptedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"actualBurned","type":"uint256"},{"indexed":false,"internalType":"string","name":"method","type":"string"}],"name":"TokenBurnMethodUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensBurned","type":"event"},{"inputs":[{"internalType":"address","name":"managerAddress","type":"address"}],"name":"addManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allGames","outputs":[{"internalType":"address","name":"gameAddress","type":"address"},{"internalType":"uint256","name":"numParticipants","type":"uint256"},{"internalType":"uint256","name":"totalBurned","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"prizeAmount","type":"uint256"},{"internalType":"uint256","name":"finalPrize","type":"uint256"},{"internalType":"uint256","name":"tokenDecimals","type":"uint256"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"string","name":"tag","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint64","name":"_endTimestamp","type":"uint64"},{"internalType":"uint256","name":"_prizeAmount","type":"uint256"},{"internalType":"string","name":"_tag","type":"string"}],"name":"createGame","outputs":[{"internalType":"address","name":"gameAddress","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gameAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositIntoGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"entropyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gameIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllGames","outputs":[{"components":[{"internalType":"address","name":"gameAddress","type":"address"},{"internalType":"uint256","name":"numParticipants","type":"uint256"},{"internalType":"uint256","name":"totalBurned","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"prizeAmount","type":"uint256"},{"internalType":"uint256","name":"finalPrize","type":"uint256"},{"internalType":"uint256","name":"tokenDecimals","type":"uint256"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"string","name":"tag","type":"string"}],"internalType":"struct GameFactory.GameInfo[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"managers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"managerAddress","type":"address"}],"name":"removeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"updateAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newEntropyAddress","type":"address"}],"name":"updateEntropyAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_finalPrize","type":"uint256"}],"name":"updateFinalPrize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"numParticipants","type":"uint256"},{"internalType":"uint256","name":"totalBurned","type":"uint256"}],"name":"updateGameInfo","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
6080604052348015600e575f80fd5b50604051614797380380614797833981016040819052602b916096565b60015f556001600160a01b038116605557604051633ef39b8160e01b815260040160405180910390fd5b600180546001600160a01b039092166001600160a01b0319928316179055600380549091167336825bf3fbdf5a29e2d5148bfe7dcf7b5639e32017905560c1565b5f6020828403121560a5575f80fd5b81516001600160a01b038116811460ba575f80fd5b9392505050565b6146c9806100ce5f395ff3fe608060405234801561000f575f80fd5b50600436106100e5575f3560e01c8063a5a48d6b11610088578063db1c45f911610063578063db1c45f9146101f6578063e2f273bd1461020b578063f851a4401461021e578063fdff9b4d14610231575f80fd5b8063a5a48d6b146101a3578063ac18de43146101d0578063b4155cda146101e3575f80fd5b80634bb7b599116100c35780634bb7b599146101245780636aff416114610154578063a0b5509514610167578063a1c6579614610190575f80fd5b80632674ccb1146100e95780632d06177a146100fe578063439c061914610111575b5f80fd5b6100fc6100f7366004611a01565b610263565b005b6100fc61010c366004611a3c565b61031a565b6100fc61011f366004611a3c565b61038f565b600354610137906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100fc610162366004611a5c565b610403565b61017a610175366004611a84565b6106f7565b60405161014b9a99989796959493929190611ac9565b6100fc61019e366004611a84565b610875565b6101c26101b1366004611a3c565b60056020525f908152604090205481565b60405190815260200161014b565b6100fc6101de366004611a3c565b6108e4565b6101376101f1366004611b42565b610956565b6101fe610e3e565b60405161014b9190611be5565b6100fc610219366004611a3c565b611022565b600154610137906001600160a01b031681565b61025361023f366004611a3c565b60026020525f908152604090205460ff1681565b604051901515815260200161014b565b335f9081526004602052604090205460ff166102925760405163032d9f6960e01b815260040160405180910390fd5b335f9081526005602052604081205460068054919291839081106102b8576102b8611cdf565b5f9182526020918290206001600a90920201908101869055600281018590556040805187815292830186905290925033917fb280562fbb964e0eeaef84ad137d0cfc7f317ff451c25d3289887700ff53c62a910160405180910390a250505050565b6001546001600160a01b0316331461034557604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b03811661036c57604051636ba0d85d60e11b815260040160405180910390fd5b6001600160a01b03165f908152600260205260409020805460ff19166001179055565b6001546001600160a01b031633146103ba57604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b0381166103e1576040516349cf5f6f60e11b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b61040b611096565b6001600160a01b0382165f9081526004602052604090205460ff166104435760405163032d9f6960e01b815260040160405180910390fd5b6001600160a01b0382165f90815260056020526040812054600680549192918390811061047257610472611cdf565b905f5260205f2090600a02019050806004015442106104a457604051634c74403d60e01b815260040160405180910390fd5b5f849050806001600160a01b0316632f6fe3966040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104e4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105089190611cf3565b156105265760405163246450af60e21b815260040160405180910390fd5b835f0361054657604051631f2a200560e01b815260040160405180910390fd5b60038201546040516370a0823160e01b81523060048201526001600160a01b03909116905f9082906370a0823190602401602060405180830381865afa158015610592573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105b69190611d12565b90506105cd6001600160a01b0383163330896110f2565b6040516370a0823160e01b81523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa158015610611573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106359190611d12565b90505f6106428383611d29565b90505f61064f8583611163565b60405181815290915033907ffd38818f5291bf0bb3a2a48aadc06ba8757865d1dabd804585338aab3009dcb69060200160405180910390a2604051630f2bc5d760e11b8152336004820152602481018290526001600160a01b03871690631e578bae906044015f604051808303815f87803b1580156106cc575f80fd5b505af11580156106de573d5f803e3d5ffd5b5050505050505050505050506106f360015f55565b5050565b60068181548110610706575f80fd5b5f9182526020909120600a90910201805460018201546002830154600384015460048501546005860154600687015460078801546008890180546001600160a01b03998a169b5097999698909516969395929491939092919061076890611d48565b80601f016020809104026020016040519081016040528092919081815260200182805461079490611d48565b80156107df5780601f106107b6576101008083540402835291602001916107df565b820191905f5260205f20905b8154815290600101906020018083116107c257829003601f168201915b5050505050908060090180546107f490611d48565b80601f016020809104026020016040519081016040528092919081815260200182805461082090611d48565b801561086b5780601f106108425761010080835404028352916020019161086b565b820191905f5260205f20905b81548152906001019060200180831161084e57829003601f168201915b505050505090508a565b335f9081526004602052604090205460ff166108a45760405163032d9f6960e01b815260040160405180910390fd5b335f9081526005602052604081205460068054919291839081106108ca576108ca611cdf565b5f91825260209091206006600a9092020101929092555050565b6001546001600160a01b0316331461090f57604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b03811661093657604051636ba0d85d60e11b815260040160405180910390fd5b6001600160a01b03165f908152600260205260409020805460ff19169055565b5f61095f611096565b6001546001600160a01b031633148015906109895750335f9081526002602052604090205460ff16155b156109a75760405163034daddb60e41b815260040160405180910390fd5b428567ffffffffffffffff16116109d157604051634c74403d60e01b815260040160405180910390fd5b835f036109f157604051632f2c7a9760e01b815260040160405180910390fd5b5f868660035f9054906101000a90046001600160a01b0316338787604051610a18906119f4565b610a2796959493929190611d80565b604051809103905ff080158015610a40573d5f803e3d5ffd5b506040516370a0823160e01b81526001600160a01b03808316600483015291925088915f91908316906370a0823190602401602060405180830381865afa158015610a8d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab19190611d12565b9050610ac86001600160a01b03831633858a6110f2565b6040516370a0823160e01b81526001600160a01b0384811660048301525f91908416906370a0823190602401602060405180830381865afa158015610b0f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b339190611d12565b90505f610b408383611d29565b9050600160045f876001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548160ff0219169083151502179055506006604051806101400160405280876001600160a01b031681526020015f81526020015f81526020018d6001600160a01b031681526020018c67ffffffffffffffff1681526020018381526020015f81526020018d6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c319190611de2565b60ff1681526020018d6001600160a01b03166395d89b416040518163ffffffff1660e01b81526004015f60405180830381865afa158015610c74573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610c9b9190810190611e16565b81526020018a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201829052509390945250508354600180820186559482526020918290208451600a9092020180546001600160a01b03199081166001600160a01b0393841617825592850151958101959095556040840151600286015560608401516003860180549093169116179055506080810151600483015560a0810151600583015560c0810151600683015560e081015160078301556101008101519091906008820190610d769082611f14565b506101208201516009820190610d8c9082611f14565b50506006545f9150610da090600190611d29565b90508060055f886001600160a01b03166001600160a01b031681526020019081526020015f2081905550336001600160a01b0316866001600160a01b03167fd3432ff5c78a4cfac45492c26900080695bc03e553bf581d99afdee4869c3e718e604051610e1c91906001600160a01b0391909116815260200190565b60405180910390a35093945050505050610e3560015f55565b95945050505050565b60606006805480602002602001604051908101604052809291908181526020015f905b82821015611019575f8481526020908190206040805161014081018252600a860290920180546001600160a01b0390811684526001820154948401949094526002810154918301919091526003810154909216606082015260048201546080820152600582015460a0820152600682015460c0820152600782015460e082015260088201805491929161010084019190610efa90611d48565b80601f0160208091040260200160405190810160405280929190818152602001828054610f2690611d48565b8015610f715780601f10610f4857610100808354040283529160200191610f71565b820191905f5260205f20905b815481529060010190602001808311610f5457829003601f168201915b50505050508152602001600982018054610f8a90611d48565b80601f0160208091040260200160405190810160405280929190818152602001828054610fb690611d48565b80156110015780601f10610fd857610100808354040283529160200191611001565b820191905f5260205f20905b815481529060010190602001808311610fe457829003601f168201915b50505050508152505081526020019060010190610e61565b50505050905090565b6001546001600160a01b0316331461104d57604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b03811661107457604051633ef39b8160e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60025f54036110ec5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60025f55565b6040516001600160a01b038085166024830152831660448201526064810182905261115d9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526116c9565b50505050565b6040516370a0823160e01b81523060048201525f9081906001600160a01b038516906370a0823190602401602060405180830381865afa1580156111a9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111cd9190611d12565b90505f6111d98561179f565b1561138b57604051630852cd8d60e31b8152600481018590526001600160a01b038616906342966c68906024015f604051808303815f87803b15801561121d575f80fd5b505af192505050801561122e575060015b6112a057846001600160a01b03167fb243711da5d4f2dffc4f91130901c5c946b71d833b5e2cc09f57cd085806a8f760405161129390602080825260159082015274109d5c9b919d5b98dd1a5bdb881c995d995c9d1959605a1b604082015260600190565b60405180910390a26113fd565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa1580156112e2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113069190611d12565b6113109083611d29565b9050846001600160a01b03167f13a34d1f55ac5d694a58693186ba5757758a7c1381c9b9d26ec128747c5bec91858360405161137a9291909182526020820152606060408201819052600c908201526b213ab937233ab731ba34b7b760a11b608082015260a00190565b60405180910390a291506116c39050565b846001600160a01b03167fb243711da5d4f2dffc4f91130901c5c946b71d833b5e2cc09f57cd085806a8f76040516113f4906020808252601a908201527f4275726e46756e6374696f6e206e6f7420737570706f72746564000000000000604082015260600190565b60405180910390a25b604080515f60248201819052604480830188905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b179052915182916001600160a01b038916916114559190611fcf565b5f604051808303815f865af19150503d805f811461148e576040519150601f19603f3d011682016040523d82523d5f602084013e611493565b606091505b50915091505f8280156114be5750815115806114be5750818060200190518101906114be9190611cf3565b905080156115bc576040516370a0823160e01b81523060048201526001600160a01b038916906370a0823190602401602060405180830381865afa158015611508573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061152c9190611d12565b6115369086611d29565b9350876001600160a01b03167f13a34d1f55ac5d694a58693186ba5757758a7c1381c9b9d26ec128747c5bec9188866040516115a79291909182526020820152606060408201819052601390820152722d32b937a0b2323932b9b9aa3930b739b332b960691b608082015260a00190565b60405180910390a283955050505050506116c3565b506115d69150506001600160a01b03861661dead8661183a565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa158015611618573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061163c9190611d12565b6116469083611d29565b9050846001600160a01b03167f13a34d1f55ac5d694a58693186ba5757758a7c1381c9b9d26ec128747c5bec9185836040516116b79291909182526020820152606060408201819052601390820152722232b0b220b2323932b9b9aa3930b739b332b960691b608082015260a00190565b60405180910390a29150505b92915050565b5f61171d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661186a9092919063ffffffff16565b80519091501561179a578080602001905181019061173b9190611cf3565b61179a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016110e3565b505050565b60408051600160248083019190915282518083039091018152604490910182526020810180516001600160e01b0316630852cd8d60e31b17905290515f9182916001600160a01b038516916117f391611fcf565b5f60405180830381855afa9150503d805f811461182b576040519150601f19603f3d011682016040523d82523d5f602084013e611830565b606091505b5090949350505050565b6040516001600160a01b03831660248201526044810182905261179a90849063a9059cbb60e01b90606401611126565b606061187884845f85611880565b949350505050565b6060824710156118e15760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016110e3565b5f80866001600160a01b031685876040516118fc9190611fcf565b5f6040518083038185875af1925050503d805f8114611936576040519150601f19603f3d011682016040523d82523d5f602084013e61193b565b606091505b509150915061194c87838387611957565b979650505050505050565b606083156119c55782515f036119be576001600160a01b0385163b6119be5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016110e3565b5081611878565b61187883838151156119da5781518083602001fd5b8060405162461bcd60e51b81526004016110e39190611fe5565b61269c80611ff883390190565b5f8060408385031215611a12575f80fd5b50508035926020909101359150565b80356001600160a01b0381168114611a37575f80fd5b919050565b5f60208284031215611a4c575f80fd5b611a5582611a21565b9392505050565b5f8060408385031215611a6d575f80fd5b611a7683611a21565b946020939093013593505050565b5f60208284031215611a94575f80fd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b60018060a01b038b16815289602082015288604082015260018060a01b03881660608201528660808201528560a08201528460c08201528360e08201526101406101008201525f611b1e610140830185611a9b565b828103610120840152611b318185611a9b565b9d9c50505050505050505050505050565b5f805f805f60808688031215611b56575f80fd5b611b5f86611a21565b9450602086013567ffffffffffffffff81168114611b7b575f80fd5b935060408601359250606086013567ffffffffffffffff811115611b9d575f80fd5b8601601f81018813611bad575f80fd5b803567ffffffffffffffff811115611bc3575f80fd5b886020828401011115611bd4575f80fd5b959894975092955050506020019190565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b82811015611cd357868503603f19018452815180516001600160a01b0316865260208101516020870152604081015160408701526060810151611c5960608801826001600160a01b03169052565b506080810151608087015260a081015160a087015260c081015160c087015260e081015160e0870152610100810151610140610100880152611c9f610140880182611a9b565b90506101208201519150868103610120880152611cbc8183611a9b565b965050506020938401939190910190600101611c0b565b50929695505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215611d03575f80fd5b81518015158114611a55575f80fd5b5f60208284031215611d22575f80fd5b5051919050565b818103818111156116c357634e487b7160e01b5f52601160045260245ffd5b600181811c90821680611d5c57607f821691505b602082108103611d7a57634e487b7160e01b5f52602260045260245ffd5b50919050565b6001600160a01b03878116825267ffffffffffffffff8716602083015285811660408301528416606082015260a0608082018190528101829052818360c08301375f81830160c090810191909152601f909201601f1916010195945050505050565b5f60208284031215611df2575f80fd5b815160ff81168114611a55575f80fd5b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215611e26575f80fd5b815167ffffffffffffffff811115611e3c575f80fd5b8201601f81018413611e4c575f80fd5b805167ffffffffffffffff811115611e6657611e66611e02565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715611e9557611e95611e02565b604052818152828201602001861015611eac575f80fd5b8160208401602083015e5f91810160200191909152949350505050565b601f82111561179a57805f5260205f20601f840160051c81016020851015611eee5750805b601f840160051c820191505b81811015611f0d575f8155600101611efa565b5050505050565b815167ffffffffffffffff811115611f2e57611f2e611e02565b611f4281611f3c8454611d48565b84611ec9565b6020601f821160018114611f74575f8315611f5d5750848201515b5f19600385901b1c1916600184901b178455611f0d565b5f84815260208120601f198516915b82811015611fa35787850151825560209485019460019092019101611f83565b5084821015611fc057868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f82518060208501845e5f920191825250919050565b602081525f611a556020830184611a9b56fe610140604052348015610010575f80fd5b5060405161269c38038061269c83398101604081905261002f9161022d565b60015f55426001600160401b0385161161005c57604051634c74403d60e01b815260040160405180910390fd5b6001600160a01b03808616608081905284821660a0523360c05290831660e0526001600160401b03851661010052604080516395d89b4160e01b815290516395d89b41916004808201925f929091908290030181865afa1580156100c2573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526100e991908101906102b6565b6001906100f69082610373565b50846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610133573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610157919061042d565b60ff1661012052600661016a8282610373565b505050505050610454565b80516001600160a01b038116811461018b575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f8301126101b3575f80fd5b81516001600160401b038111156101cc576101cc610190565b604051601f8201601f19908116603f011681016001600160401b03811182821017156101fa576101fa610190565b604052818152838201602001851015610211575f80fd5b8160208501602083015e5f918101602001919091529392505050565b5f805f805f60a08688031215610241575f80fd5b61024a86610175565b60208701519095506001600160401b0381168114610266575f80fd5b935061027460408701610175565b925061028260608701610175565b60808701519092506001600160401b0381111561029d575f80fd5b6102a9888289016101a4565b9150509295509295909350565b5f602082840312156102c6575f80fd5b81516001600160401b038111156102db575f80fd5b6102e7848285016101a4565b949350505050565b600181811c9082168061030357607f821691505b60208210810361032157634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561036e57805f5260205f20601f840160051c8101602085101561034c5750805b601f840160051c820191505b8181101561036b575f8155600101610358565b50505b505050565b81516001600160401b0381111561038c5761038c610190565b6103a08161039a84546102ef565b84610327565b6020601f8211600181146103d2575f83156103bb5750848201515b5f19600385901b1c1916600184901b17845561036b565b5f84815260208120601f198516915b8281101561040157878501518255602094850194600190920191016103e1565b508482101561041e57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f6020828403121561043d575f80fd5b815160ff8116811461044d575f80fd5b9392505050565b60805160a05160c05160e05161010051610120516121756105275f395f818161025401526109cc01525f81816103ba015281816108a8015281816108da01526113c701525f81816101e80152818161126c015261129301525f818161041a015281816105320152818161079e01526111bf01525f818161029901528181610f7b01528181611439015281816114cb015281816115c5015281816116dd015261177d01525f81816104fd0152818161091701528181610a11015281816110bf0152818161124a015261130a01526121755ff3fe608060405260043610610161575f3560e01c806370740ac9116100cd578063caba2d8711610087578063ced72f8711610062578063ced72f871461049f578063d89135cd146104b3578063dfbf53ae146104c8578063fc0c546a146104ec575f80fd5b8063caba2d871461043c578063cc364f4814610455578063ce653d5f14610474575f80fd5b806370740ac91461036e5780637b61c320146103825780638fdd3d6b14610396578063a85adeab146103a9578063b135a145146103f5578063c45a015514610409575f80fd5b806347ce07cc1161011e57806347ce07cc146102885780634e76a846146102bb57806351f91066146102d057806352a5f1f8146102f15780635fdecdfa146103105780636c66cbb91461034e575f80fd5b80631e578bae1461016557806320a0484a146101865780632f6fe396146101ae57806337b1b229146101d7578063382396ee146102225780633b97e85614610243575b5f80fd5b348015610170575f80fd5b5061018461017f366004611d04565b61051f565b005b348015610191575f80fd5b5061019b60075481565b6040519081526020015b60405180910390f35b3480156101b9575f80fd5b506002546101c79060ff1681565b60405190151581526020016101a5565b3480156101e2575f80fd5b5061020a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101a5565b34801561022d575f80fd5b50610236610803565b6040516101a59190611dcf565b34801561024e575f80fd5b506102767f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016101a5565b348015610293575f80fd5b5061020a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102c6575f80fd5b5061019b60045481565b3480156102db575f80fd5b506102e4610eed565b6040516101a59190611f02565b3480156102fc575f80fd5b5061018461030b366004611f30565b610f79565b34801561031b575f80fd5b5061032f61032a366004611f6e565b61106a565b604080516001600160a01b0390931683526020830191909152016101a5565b348015610359575f80fd5b506005546101c790600160a81b900460ff1681565b348015610379575f80fd5b506101846110a0565b34801561038d575f80fd5b506102e461138c565b6101846103a4366004611f6e565b611399565b3480156103b4575f80fd5b506103dc7f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff90911681526020016101a5565b348015610400575f80fd5b5060085461019b565b348015610414575f80fd5b5061020a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610447575f80fd5b506005546101c79060ff1681565b348015610460575f80fd5b5061032f61046f366004611f6e565b611683565b34801561047f575f80fd5b5061019b61048e366004611f85565b60096020525f908152604090205481565b3480156104aa575f80fd5b5061019b6116d9565b3480156104be575f80fd5b5061019b60035481565b3480156104d3575f80fd5b5060055461020a9061010090046001600160a01b031681565b3480156104f7575f80fd5b5061020a7f000000000000000000000000000000000000000000000000000000000000000081565b6105276117f7565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461057057604051631966391b60e11b815260040160405180910390fd5b6001600160a01b0382165f9081526009602052604081205490036105a35760048054905f61059d83611fb4565b91905055505b6001600160a01b0382165f90815260096020526040812080548392906105ca908490611fcc565b925050819055508060035f8282546105e29190611fcc565b90915550506003545f906105f890600190611fe5565b6008549091501580159061064e5750600880546001600160a01b038516919061062390600190611fe5565b8154811061063357610633611ff8565b5f9182526020909120600290910201546001600160a01b0316145b1561069e576008805483919061066690600190611fe5565b8154811061067657610676611ff8565b905f5260205f2090600202016001015f8282546106939190611fcc565b909155506107319050565b604080518082019091526001600160a01b03848116825260208201838152600880546001810182555f9190915292517ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3600290940293840180546001600160a01b0319169190931617909155517ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee4909101555b826001600160a01b03167f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df78360405161076c91815260200190565b60405180910390a260048054600354604051632674ccb160e01b81529283019190915260248201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632674ccb1906044015f604051808303815f87803b1580156107df575f80fd5b505af11580156107f1573d5f803e3d5ffd5b50505050506107ff60015f55565b5050565b610886604051806101c001604052805f151581526020015f151581526020015f815260200160608152602001606081526020015f6001600160a01b03168152602001606081526020015f60ff1681526020015f81526020015f81526020015f6001600160a01b031681526020015f151581526020015f8152602001606081525090565b60025460ff90811615158252600554161515602082015267ffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001642106108d4575f6108fe565b6108fe427f000000000000000000000000000000000000000000000000000000000000000061200c565b67ffffffffffffffff1660408201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660a0820152600180546109499061202c565b80601f01602080910402602001604051908101604052809291908181526020018280546109759061202c565b80156109c05780601f10610997576101008083540402835291602001916109c0565b820191905f5260205f20905b8154815290600101906020018083116109a357829003601f168201915b505050505060c08201527f000000000000000000000000000000000000000000000000000000000000000060ff1660e08201526003546101008201526040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610a5e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a829190612064565b61012082015260055461010081046001600160a01b0316610140830152600160a81b900460ff16151561016082015260075461018082015260068054610ac79061202c565b80601f0160208091040260200160405190810160405280929190818152602001828054610af39061202c565b8015610b3e5780601f10610b1557610100808354040283529160200191610b3e565b820191905f5260205f20905b815481529060010190602001808311610b2157829003601f168201915b50505050506101a08201526008545f819003610b7f5750604080515f8082526020808301845260608501929092528251908152908101909152608082015290565b5f8167ffffffffffffffff811115610b9957610b9961207b565b604051908082528060200260200182016040528015610bc2578160200160208202803683370190505b5090505f8267ffffffffffffffff811115610bdf57610bdf61207b565b604051908082528060200260200182016040528015610c08578160200160208202803683370190505b5090505f805b84811015610db4575f60088281548110610c2a57610c2a611ff8565b5f91825260208083206040805180820190915260029093020180546001600160a01b03168352600101549082015291508215610c9e576008610c6d600185611fe5565b81548110610c7d57610c7d611ff8565b905f5260205f209060020201600101546001610c999190611fcc565b610ca0565b5f5b60208301519091505f610cb38383611fe5565b610cbe906001611fcc565b90505f805b87811015610d3b57855f01516001600160a01b03168a8281518110610cea57610cea611ff8565b60200260200101516001600160a01b031603610d335782898281518110610d1357610d13611ff8565b60200260200101818151610d279190611fcc565b90525060019150610d3b565b600101610cc3565b5080610da357845f0151898881518110610d5757610d57611ff8565b60200260200101906001600160a01b031690816001600160a01b03168152505081888881518110610d8a57610d8a611ff8565b602090810291909101015286610d9f81611fb4565b9750505b505060019093019250610c0e915050565b505f8167ffffffffffffffff811115610dcf57610dcf61207b565b604051908082528060200260200182016040528015610df8578160200160208202803683370190505b5090505f8267ffffffffffffffff811115610e1557610e1561207b565b604051908082528060200260200182016040528015610e3e578160200160208202803683370190505b5090505f5b83811015610ed657858181518110610e5d57610e5d611ff8565b6020026020010151838281518110610e7757610e77611ff8565b60200260200101906001600160a01b031690816001600160a01b031681525050848181518110610ea957610ea9611ff8565b6020026020010151828281518110610ec357610ec3611ff8565b6020908102919091010152600101610e43565b506060870191909152608086015250929392505050565b60068054610efa9061202c565b80601f0160208091040260200160405190810160405280929190818152602001828054610f269061202c565b8015610f715780601f10610f4857610100808354040283529160200191610f71565b820191905f5260205f20905b815481529060010190602001808311610f5457829003601f168201915b505050505081565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038116610ff55760405162461bcd60e51b815260206004820152601760248201527f456e74726f70792061646472657373206e6f742073657400000000000000000060448201526064015b60405180910390fd5b336001600160a01b038216146110595760405162461bcd60e51b815260206004820152602360248201527f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e637460448201526234b7b760e91b6064820152608401610fec565b61106484848461184e565b50505050565b60088181548110611079575f80fd5b5f918252602090912060029091020180546001909101546001600160a01b03909116915082565b6110a86117f7565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561110c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111309190612064565b60025490915060ff166111565760405163246450af60e21b815260040160405180910390fd5b60055460ff1661117957604051631a9faa4160e01b815260040160405180910390fd5b600554600160a81b900460ff16156111a45760405163611ab18960e11b815260040160405180910390fd5b60078190556040516350e32bcb60e11b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a1c65796906024015f604051808303815f87803b158015611208575f80fd5b505af115801561121a573d5f803e3d5ffd5b50506005805460ff60a81b1916600160a81b17905550506003545f919082036112f9576112916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000008361198a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f5965f39b96d8d8c1d4c61686c7eb65fe448dcef689e496837c1464029298a37d826040516112ec91815260200190565b60405180910390a261137f565b600554611338906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116916101009004168361198a565b6005546040518281526101009091046001600160a01b0316907f95681e512bc0fe659e195e06c283eada494316f3d801213e48e7101af92bf7709060200160405180910390a25b505061138a60015f55565b565b60018054610efa9061202c565b6113a16117f7565b60025460ff16156113c55760405163246450af60e21b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1642101561141057604051634966314360e11b815260040160405180910390fd5b6002805460ff19166001179055604080516320bba64360e21b815290515f916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916382ee990c916004808201926020929091908290030181865afa158015611483573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114a7919061208f565b604051631711922960e31b81526001600160a01b0380831660048301529192505f917f0000000000000000000000000000000000000000000000000000000000000000169063b88c914890602401602060405180830381865afa158015611510573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061153491906120aa565b6001600160801b03169050803410156115605760405163732f941360e01b815260040160405180910390fd5b8034111561159d57336108fc6115768334611fe5565b6040518115909202915f818181858888f1935050505015801561159b573d5f803e3d5ffd5b505b6040516319cb825f60e01b81526001600160a01b038381166004830152602482018590525f917f0000000000000000000000000000000000000000000000000000000000000000909116906319cb825f90849060440160206040518083038185885af115801561160f573d5f803e3d5ffd5b50505050506040513d601f19601f8201168201806040525081019061163491906120d0565b60405167ffffffffffffffff821681529091507fce24807f7e4b60b4e641462f13029fa5e5f79075bbc3f8e5cd43ecd19661d7609060200160405180910390a150505061168060015f55565b50565b5f805f6008848154811061169957611699611ff8565b5f9182526020918290206040805180820190915260029092020180546001600160a01b031680835260019091015491909201819052909590945092505050565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166382ee990c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611737573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061175b919061208f565b604051631711922960e31b81526001600160a01b0380831660048301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063b88c914890602401602060405180830381865afa1580156117c4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117e891906120aa565b6001600160801b031691505090565b60025f54036118485760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610fec565b60025f55565b60025460ff166118715760405163246450af60e21b815260040160405180910390fd5b60055460ff161561189557604051634a4117f960e01b815260040160405180910390fd5b6005805460ff191660011790556003545f8190036118c35760058054610100600160a81b0319169055611901565b5f6118ce82846120eb565b90506118d9816119e1565b600560016101000a8154816001600160a01b0302191690836001600160a01b03160217905550505b6005546040516101009091046001600160a01b0316907f1d4c260f1824cd028e6c9e6e31c3a0b94f2513e7a641113ec759d382f9bdd5a1905f90a260405167ffffffffffffffff851681526001600160a01b038416907fd299755aa265bbec2e80e44e14c3ae068c6505f620a5e28073bc3a0c02eca2e09060200160405180910390a250505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526119dc908490611a95565b505050565b6008545f90819081906119f690600190611fe5565b90505b808211611a7c575f6001611a0d8385611fcc565b901c90505f60088281548110611a2557611a25611ff8565b5f9182526020918290206040805180820190915260029092020180546001600160a01b031682526001015491810182905291508611611a68575195945050505050565b611a73826001611fcc565b935050506119f9565b604051633cf0cfff60e11b815260040160405180910390fd5b5f611ae9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611b669092919063ffffffff16565b8051909150156119dc5780806020019051810190611b07919061210a565b6119dc5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610fec565b6060611b7484845f85611b7c565b949350505050565b606082471015611bdd5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610fec565b5f80866001600160a01b03168587604051611bf89190612129565b5f6040518083038185875af1925050503d805f8114611c32576040519150601f19603f3d011682016040523d82523d5f602084013e611c37565b606091505b5091509150611c4887838387611c53565b979650505050505050565b60608315611cc15782515f03611cba576001600160a01b0385163b611cba5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610fec565b5081611b74565b611b748383815115611cd65781518083602001fd5b8060405162461bcd60e51b8152600401610fec9190611f02565b6001600160a01b0381168114611680575f80fd5b5f8060408385031215611d15575f80fd5b8235611d2081611cf0565b946020939093013593505050565b5f8151808452602084019350602083015f5b82811015611d675781516001600160a01b0316865260209586019590910190600101611d40565b5093949350505050565b5f8151808452602084019350602083015f5b82811015611d67578151865260209586019590910190600101611d83565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b60208152611de260208201835115159052565b5f6020830151611df6604084018215159052565b506040830151606083015260608301516101c06080840152611e1c6101e0840182611d2e565b90506080840151601f198483030160a0850152611e398282611d71565b91505060a0840151611e5660c08501826001600160a01b03169052565b5060c0840151838203601f190160e0850152611e728282611da1565b91505060e0840151611e8a61010085018260ff169052565b50610100840151610120840152610120840151610140840152610140840151611ebf6101608501826001600160a01b03169052565b50610160840151801515610180850152506101808401516101a08401526101a0840151601f19848303016101c0850152611ef98282611da1565b95945050505050565b602081525f611f146020830184611da1565b9392505050565b67ffffffffffffffff81168114611680575f80fd5b5f805f60608486031215611f42575f80fd5b8335611f4d81611f1b565b92506020840135611f5d81611cf0565b929592945050506040919091013590565b5f60208284031215611f7e575f80fd5b5035919050565b5f60208284031215611f95575f80fd5b8135611f1481611cf0565b634e487b7160e01b5f52601160045260245ffd5b5f60018201611fc557611fc5611fa0565b5060010190565b80820180821115611fdf57611fdf611fa0565b92915050565b81810381811115611fdf57611fdf611fa0565b634e487b7160e01b5f52603260045260245ffd5b67ffffffffffffffff8281168282160390811115611fdf57611fdf611fa0565b600181811c9082168061204057607f821691505b60208210810361205e57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f60208284031215612074575f80fd5b5051919050565b634e487b7160e01b5f52604160045260245ffd5b5f6020828403121561209f575f80fd5b8151611f1481611cf0565b5f602082840312156120ba575f80fd5b81516001600160801b0381168114611f14575f80fd5b5f602082840312156120e0575f80fd5b8151611f1481611f1b565b5f8261210557634e487b7160e01b5f52601260045260245ffd5b500690565b5f6020828403121561211a575f80fd5b81518015158114611f14575f80fd5b5f82518060208501845e5f92019182525091905056fea264697066735822122082003ecd4e50af55bf42bd2679a1d2a00b365c0bf737b2c1de353a78e2e43e8864736f6c634300081a0033a264697066735822122073808df5cd239849be4c7bbd66b2d0e17a6860f03d409273d7001e6f845fab1064736f6c634300081a00330000000000000000000000003926babb9ef6c3588a7b2c7866e74ed36702c5ac
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106100e5575f3560e01c8063a5a48d6b11610088578063db1c45f911610063578063db1c45f9146101f6578063e2f273bd1461020b578063f851a4401461021e578063fdff9b4d14610231575f80fd5b8063a5a48d6b146101a3578063ac18de43146101d0578063b4155cda146101e3575f80fd5b80634bb7b599116100c35780634bb7b599146101245780636aff416114610154578063a0b5509514610167578063a1c6579614610190575f80fd5b80632674ccb1146100e95780632d06177a146100fe578063439c061914610111575b5f80fd5b6100fc6100f7366004611a01565b610263565b005b6100fc61010c366004611a3c565b61031a565b6100fc61011f366004611a3c565b61038f565b600354610137906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6100fc610162366004611a5c565b610403565b61017a610175366004611a84565b6106f7565b60405161014b9a99989796959493929190611ac9565b6100fc61019e366004611a84565b610875565b6101c26101b1366004611a3c565b60056020525f908152604090205481565b60405190815260200161014b565b6100fc6101de366004611a3c565b6108e4565b6101376101f1366004611b42565b610956565b6101fe610e3e565b60405161014b9190611be5565b6100fc610219366004611a3c565b611022565b600154610137906001600160a01b031681565b61025361023f366004611a3c565b60026020525f908152604090205460ff1681565b604051901515815260200161014b565b335f9081526004602052604090205460ff166102925760405163032d9f6960e01b815260040160405180910390fd5b335f9081526005602052604081205460068054919291839081106102b8576102b8611cdf565b5f9182526020918290206001600a90920201908101869055600281018590556040805187815292830186905290925033917fb280562fbb964e0eeaef84ad137d0cfc7f317ff451c25d3289887700ff53c62a910160405180910390a250505050565b6001546001600160a01b0316331461034557604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b03811661036c57604051636ba0d85d60e11b815260040160405180910390fd5b6001600160a01b03165f908152600260205260409020805460ff19166001179055565b6001546001600160a01b031633146103ba57604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b0381166103e1576040516349cf5f6f60e11b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b61040b611096565b6001600160a01b0382165f9081526004602052604090205460ff166104435760405163032d9f6960e01b815260040160405180910390fd5b6001600160a01b0382165f90815260056020526040812054600680549192918390811061047257610472611cdf565b905f5260205f2090600a02019050806004015442106104a457604051634c74403d60e01b815260040160405180910390fd5b5f849050806001600160a01b0316632f6fe3966040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104e4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105089190611cf3565b156105265760405163246450af60e21b815260040160405180910390fd5b835f0361054657604051631f2a200560e01b815260040160405180910390fd5b60038201546040516370a0823160e01b81523060048201526001600160a01b03909116905f9082906370a0823190602401602060405180830381865afa158015610592573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105b69190611d12565b90506105cd6001600160a01b0383163330896110f2565b6040516370a0823160e01b81523060048201525f906001600160a01b038416906370a0823190602401602060405180830381865afa158015610611573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106359190611d12565b90505f6106428383611d29565b90505f61064f8583611163565b60405181815290915033907ffd38818f5291bf0bb3a2a48aadc06ba8757865d1dabd804585338aab3009dcb69060200160405180910390a2604051630f2bc5d760e11b8152336004820152602481018290526001600160a01b03871690631e578bae906044015f604051808303815f87803b1580156106cc575f80fd5b505af11580156106de573d5f803e3d5ffd5b5050505050505050505050506106f360015f55565b5050565b60068181548110610706575f80fd5b5f9182526020909120600a90910201805460018201546002830154600384015460048501546005860154600687015460078801546008890180546001600160a01b03998a169b5097999698909516969395929491939092919061076890611d48565b80601f016020809104026020016040519081016040528092919081815260200182805461079490611d48565b80156107df5780601f106107b6576101008083540402835291602001916107df565b820191905f5260205f20905b8154815290600101906020018083116107c257829003601f168201915b5050505050908060090180546107f490611d48565b80601f016020809104026020016040519081016040528092919081815260200182805461082090611d48565b801561086b5780601f106108425761010080835404028352916020019161086b565b820191905f5260205f20905b81548152906001019060200180831161084e57829003601f168201915b505050505090508a565b335f9081526004602052604090205460ff166108a45760405163032d9f6960e01b815260040160405180910390fd5b335f9081526005602052604081205460068054919291839081106108ca576108ca611cdf565b5f91825260209091206006600a9092020101929092555050565b6001546001600160a01b0316331461090f57604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b03811661093657604051636ba0d85d60e11b815260040160405180910390fd5b6001600160a01b03165f908152600260205260409020805460ff19169055565b5f61095f611096565b6001546001600160a01b031633148015906109895750335f9081526002602052604090205460ff16155b156109a75760405163034daddb60e41b815260040160405180910390fd5b428567ffffffffffffffff16116109d157604051634c74403d60e01b815260040160405180910390fd5b835f036109f157604051632f2c7a9760e01b815260040160405180910390fd5b5f868660035f9054906101000a90046001600160a01b0316338787604051610a18906119f4565b610a2796959493929190611d80565b604051809103905ff080158015610a40573d5f803e3d5ffd5b506040516370a0823160e01b81526001600160a01b03808316600483015291925088915f91908316906370a0823190602401602060405180830381865afa158015610a8d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ab19190611d12565b9050610ac86001600160a01b03831633858a6110f2565b6040516370a0823160e01b81526001600160a01b0384811660048301525f91908416906370a0823190602401602060405180830381865afa158015610b0f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b339190611d12565b90505f610b408383611d29565b9050600160045f876001600160a01b03166001600160a01b031681526020019081526020015f205f6101000a81548160ff0219169083151502179055506006604051806101400160405280876001600160a01b031681526020015f81526020015f81526020018d6001600160a01b031681526020018c67ffffffffffffffff1681526020018381526020015f81526020018d6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c319190611de2565b60ff1681526020018d6001600160a01b03166395d89b416040518163ffffffff1660e01b81526004015f60405180830381865afa158015610c74573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610c9b9190810190611e16565b81526020018a8a8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201829052509390945250508354600180820186559482526020918290208451600a9092020180546001600160a01b03199081166001600160a01b0393841617825592850151958101959095556040840151600286015560608401516003860180549093169116179055506080810151600483015560a0810151600583015560c0810151600683015560e081015160078301556101008101519091906008820190610d769082611f14565b506101208201516009820190610d8c9082611f14565b50506006545f9150610da090600190611d29565b90508060055f886001600160a01b03166001600160a01b031681526020019081526020015f2081905550336001600160a01b0316866001600160a01b03167fd3432ff5c78a4cfac45492c26900080695bc03e553bf581d99afdee4869c3e718e604051610e1c91906001600160a01b0391909116815260200190565b60405180910390a35093945050505050610e3560015f55565b95945050505050565b60606006805480602002602001604051908101604052809291908181526020015f905b82821015611019575f8481526020908190206040805161014081018252600a860290920180546001600160a01b0390811684526001820154948401949094526002810154918301919091526003810154909216606082015260048201546080820152600582015460a0820152600682015460c0820152600782015460e082015260088201805491929161010084019190610efa90611d48565b80601f0160208091040260200160405190810160405280929190818152602001828054610f2690611d48565b8015610f715780601f10610f4857610100808354040283529160200191610f71565b820191905f5260205f20905b815481529060010190602001808311610f5457829003601f168201915b50505050508152602001600982018054610f8a90611d48565b80601f0160208091040260200160405190810160405280929190818152602001828054610fb690611d48565b80156110015780601f10610fd857610100808354040283529160200191611001565b820191905f5260205f20905b815481529060010190602001808311610fe457829003601f168201915b50505050508152505081526020019060010190610e61565b50505050905090565b6001546001600160a01b0316331461104d57604051637bfa4b9f60e01b815260040160405180910390fd5b6001600160a01b03811661107457604051633ef39b8160e01b815260040160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60025f54036110ec5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b60025f55565b6040516001600160a01b038085166024830152831660448201526064810182905261115d9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526116c9565b50505050565b6040516370a0823160e01b81523060048201525f9081906001600160a01b038516906370a0823190602401602060405180830381865afa1580156111a9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111cd9190611d12565b90505f6111d98561179f565b1561138b57604051630852cd8d60e31b8152600481018590526001600160a01b038616906342966c68906024015f604051808303815f87803b15801561121d575f80fd5b505af192505050801561122e575060015b6112a057846001600160a01b03167fb243711da5d4f2dffc4f91130901c5c946b71d833b5e2cc09f57cd085806a8f760405161129390602080825260159082015274109d5c9b919d5b98dd1a5bdb881c995d995c9d1959605a1b604082015260600190565b60405180910390a26113fd565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa1580156112e2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113069190611d12565b6113109083611d29565b9050846001600160a01b03167f13a34d1f55ac5d694a58693186ba5757758a7c1381c9b9d26ec128747c5bec91858360405161137a9291909182526020820152606060408201819052600c908201526b213ab937233ab731ba34b7b760a11b608082015260a00190565b60405180910390a291506116c39050565b846001600160a01b03167fb243711da5d4f2dffc4f91130901c5c946b71d833b5e2cc09f57cd085806a8f76040516113f4906020808252601a908201527f4275726e46756e6374696f6e206e6f7420737570706f72746564000000000000604082015260600190565b60405180910390a25b604080515f60248201819052604480830188905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b179052915182916001600160a01b038916916114559190611fcf565b5f604051808303815f865af19150503d805f811461148e576040519150601f19603f3d011682016040523d82523d5f602084013e611493565b606091505b50915091505f8280156114be5750815115806114be5750818060200190518101906114be9190611cf3565b905080156115bc576040516370a0823160e01b81523060048201526001600160a01b038916906370a0823190602401602060405180830381865afa158015611508573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061152c9190611d12565b6115369086611d29565b9350876001600160a01b03167f13a34d1f55ac5d694a58693186ba5757758a7c1381c9b9d26ec128747c5bec9188866040516115a79291909182526020820152606060408201819052601390820152722d32b937a0b2323932b9b9aa3930b739b332b960691b608082015260a00190565b60405180910390a283955050505050506116c3565b506115d69150506001600160a01b03861661dead8661183a565b6040516370a0823160e01b81523060048201526001600160a01b038616906370a0823190602401602060405180830381865afa158015611618573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061163c9190611d12565b6116469083611d29565b9050846001600160a01b03167f13a34d1f55ac5d694a58693186ba5757758a7c1381c9b9d26ec128747c5bec9185836040516116b79291909182526020820152606060408201819052601390820152722232b0b220b2323932b9b9aa3930b739b332b960691b608082015260a00190565b60405180910390a29150505b92915050565b5f61171d826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661186a9092919063ffffffff16565b80519091501561179a578080602001905181019061173b9190611cf3565b61179a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016110e3565b505050565b60408051600160248083019190915282518083039091018152604490910182526020810180516001600160e01b0316630852cd8d60e31b17905290515f9182916001600160a01b038516916117f391611fcf565b5f60405180830381855afa9150503d805f811461182b576040519150601f19603f3d011682016040523d82523d5f602084013e611830565b606091505b5090949350505050565b6040516001600160a01b03831660248201526044810182905261179a90849063a9059cbb60e01b90606401611126565b606061187884845f85611880565b949350505050565b6060824710156118e15760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016110e3565b5f80866001600160a01b031685876040516118fc9190611fcf565b5f6040518083038185875af1925050503d805f8114611936576040519150601f19603f3d011682016040523d82523d5f602084013e61193b565b606091505b509150915061194c87838387611957565b979650505050505050565b606083156119c55782515f036119be576001600160a01b0385163b6119be5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016110e3565b5081611878565b61187883838151156119da5781518083602001fd5b8060405162461bcd60e51b81526004016110e39190611fe5565b61269c80611ff883390190565b5f8060408385031215611a12575f80fd5b50508035926020909101359150565b80356001600160a01b0381168114611a37575f80fd5b919050565b5f60208284031215611a4c575f80fd5b611a5582611a21565b9392505050565b5f8060408385031215611a6d575f80fd5b611a7683611a21565b946020939093013593505050565b5f60208284031215611a94575f80fd5b5035919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b60018060a01b038b16815289602082015288604082015260018060a01b03881660608201528660808201528560a08201528460c08201528360e08201526101406101008201525f611b1e610140830185611a9b565b828103610120840152611b318185611a9b565b9d9c50505050505050505050505050565b5f805f805f60808688031215611b56575f80fd5b611b5f86611a21565b9450602086013567ffffffffffffffff81168114611b7b575f80fd5b935060408601359250606086013567ffffffffffffffff811115611b9d575f80fd5b8601601f81018813611bad575f80fd5b803567ffffffffffffffff811115611bc3575f80fd5b886020828401011115611bd4575f80fd5b959894975092955050506020019190565b5f602082016020835280845180835260408501915060408160051b8601019250602086015f5b82811015611cd357868503603f19018452815180516001600160a01b0316865260208101516020870152604081015160408701526060810151611c5960608801826001600160a01b03169052565b506080810151608087015260a081015160a087015260c081015160c087015260e081015160e0870152610100810151610140610100880152611c9f610140880182611a9b565b90506101208201519150868103610120880152611cbc8183611a9b565b965050506020938401939190910190600101611c0b565b50929695505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215611d03575f80fd5b81518015158114611a55575f80fd5b5f60208284031215611d22575f80fd5b5051919050565b818103818111156116c357634e487b7160e01b5f52601160045260245ffd5b600181811c90821680611d5c57607f821691505b602082108103611d7a57634e487b7160e01b5f52602260045260245ffd5b50919050565b6001600160a01b03878116825267ffffffffffffffff8716602083015285811660408301528416606082015260a0608082018190528101829052818360c08301375f81830160c090810191909152601f909201601f1916010195945050505050565b5f60208284031215611df2575f80fd5b815160ff81168114611a55575f80fd5b634e487b7160e01b5f52604160045260245ffd5b5f60208284031215611e26575f80fd5b815167ffffffffffffffff811115611e3c575f80fd5b8201601f81018413611e4c575f80fd5b805167ffffffffffffffff811115611e6657611e66611e02565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715611e9557611e95611e02565b604052818152828201602001861015611eac575f80fd5b8160208401602083015e5f91810160200191909152949350505050565b601f82111561179a57805f5260205f20601f840160051c81016020851015611eee5750805b601f840160051c820191505b81811015611f0d575f8155600101611efa565b5050505050565b815167ffffffffffffffff811115611f2e57611f2e611e02565b611f4281611f3c8454611d48565b84611ec9565b6020601f821160018114611f74575f8315611f5d5750848201515b5f19600385901b1c1916600184901b178455611f0d565b5f84815260208120601f198516915b82811015611fa35787850151825560209485019460019092019101611f83565b5084821015611fc057868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f82518060208501845e5f920191825250919050565b602081525f611a556020830184611a9b56fe610140604052348015610010575f80fd5b5060405161269c38038061269c83398101604081905261002f9161022d565b60015f55426001600160401b0385161161005c57604051634c74403d60e01b815260040160405180910390fd5b6001600160a01b03808616608081905284821660a0523360c05290831660e0526001600160401b03851661010052604080516395d89b4160e01b815290516395d89b41916004808201925f929091908290030181865afa1580156100c2573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526100e991908101906102b6565b6001906100f69082610373565b50846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610133573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610157919061042d565b60ff1661012052600661016a8282610373565b505050505050610454565b80516001600160a01b038116811461018b575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f8301126101b3575f80fd5b81516001600160401b038111156101cc576101cc610190565b604051601f8201601f19908116603f011681016001600160401b03811182821017156101fa576101fa610190565b604052818152838201602001851015610211575f80fd5b8160208501602083015e5f918101602001919091529392505050565b5f805f805f60a08688031215610241575f80fd5b61024a86610175565b60208701519095506001600160401b0381168114610266575f80fd5b935061027460408701610175565b925061028260608701610175565b60808701519092506001600160401b0381111561029d575f80fd5b6102a9888289016101a4565b9150509295509295909350565b5f602082840312156102c6575f80fd5b81516001600160401b038111156102db575f80fd5b6102e7848285016101a4565b949350505050565b600181811c9082168061030357607f821691505b60208210810361032157634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561036e57805f5260205f20601f840160051c8101602085101561034c5750805b601f840160051c820191505b8181101561036b575f8155600101610358565b50505b505050565b81516001600160401b0381111561038c5761038c610190565b6103a08161039a84546102ef565b84610327565b6020601f8211600181146103d2575f83156103bb5750848201515b5f19600385901b1c1916600184901b17845561036b565b5f84815260208120601f198516915b8281101561040157878501518255602094850194600190920191016103e1565b508482101561041e57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f6020828403121561043d575f80fd5b815160ff8116811461044d575f80fd5b9392505050565b60805160a05160c05160e05161010051610120516121756105275f395f818161025401526109cc01525f81816103ba015281816108a8015281816108da01526113c701525f81816101e80152818161126c015261129301525f818161041a015281816105320152818161079e01526111bf01525f818161029901528181610f7b01528181611439015281816114cb015281816115c5015281816116dd015261177d01525f81816104fd0152818161091701528181610a11015281816110bf0152818161124a015261130a01526121755ff3fe608060405260043610610161575f3560e01c806370740ac9116100cd578063caba2d8711610087578063ced72f8711610062578063ced72f871461049f578063d89135cd146104b3578063dfbf53ae146104c8578063fc0c546a146104ec575f80fd5b8063caba2d871461043c578063cc364f4814610455578063ce653d5f14610474575f80fd5b806370740ac91461036e5780637b61c320146103825780638fdd3d6b14610396578063a85adeab146103a9578063b135a145146103f5578063c45a015514610409575f80fd5b806347ce07cc1161011e57806347ce07cc146102885780634e76a846146102bb57806351f91066146102d057806352a5f1f8146102f15780635fdecdfa146103105780636c66cbb91461034e575f80fd5b80631e578bae1461016557806320a0484a146101865780632f6fe396146101ae57806337b1b229146101d7578063382396ee146102225780633b97e85614610243575b5f80fd5b348015610170575f80fd5b5061018461017f366004611d04565b61051f565b005b348015610191575f80fd5b5061019b60075481565b6040519081526020015b60405180910390f35b3480156101b9575f80fd5b506002546101c79060ff1681565b60405190151581526020016101a5565b3480156101e2575f80fd5b5061020a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101a5565b34801561022d575f80fd5b50610236610803565b6040516101a59190611dcf565b34801561024e575f80fd5b506102767f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016101a5565b348015610293575f80fd5b5061020a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102c6575f80fd5b5061019b60045481565b3480156102db575f80fd5b506102e4610eed565b6040516101a59190611f02565b3480156102fc575f80fd5b5061018461030b366004611f30565b610f79565b34801561031b575f80fd5b5061032f61032a366004611f6e565b61106a565b604080516001600160a01b0390931683526020830191909152016101a5565b348015610359575f80fd5b506005546101c790600160a81b900460ff1681565b348015610379575f80fd5b506101846110a0565b34801561038d575f80fd5b506102e461138c565b6101846103a4366004611f6e565b611399565b3480156103b4575f80fd5b506103dc7f000000000000000000000000000000000000000000000000000000000000000081565b60405167ffffffffffffffff90911681526020016101a5565b348015610400575f80fd5b5060085461019b565b348015610414575f80fd5b5061020a7f000000000000000000000000000000000000000000000000000000000000000081565b348015610447575f80fd5b506005546101c79060ff1681565b348015610460575f80fd5b5061032f61046f366004611f6e565b611683565b34801561047f575f80fd5b5061019b61048e366004611f85565b60096020525f908152604090205481565b3480156104aa575f80fd5b5061019b6116d9565b3480156104be575f80fd5b5061019b60035481565b3480156104d3575f80fd5b5060055461020a9061010090046001600160a01b031681565b3480156104f7575f80fd5b5061020a7f000000000000000000000000000000000000000000000000000000000000000081565b6105276117f7565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461057057604051631966391b60e11b815260040160405180910390fd5b6001600160a01b0382165f9081526009602052604081205490036105a35760048054905f61059d83611fb4565b91905055505b6001600160a01b0382165f90815260096020526040812080548392906105ca908490611fcc565b925050819055508060035f8282546105e29190611fcc565b90915550506003545f906105f890600190611fe5565b6008549091501580159061064e5750600880546001600160a01b038516919061062390600190611fe5565b8154811061063357610633611ff8565b5f9182526020909120600290910201546001600160a01b0316145b1561069e576008805483919061066690600190611fe5565b8154811061067657610676611ff8565b905f5260205f2090600202016001015f8282546106939190611fcc565b909155506107319050565b604080518082019091526001600160a01b03848116825260208201838152600880546001810182555f9190915292517ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3600290940293840180546001600160a01b0319169190931617909155517ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee4909101555b826001600160a01b03167f696de425f79f4a40bc6d2122ca50507f0efbeabbff86a84871b7196ab8ea8df78360405161076c91815260200190565b60405180910390a260048054600354604051632674ccb160e01b81529283019190915260248201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632674ccb1906044015f604051808303815f87803b1580156107df575f80fd5b505af11580156107f1573d5f803e3d5ffd5b50505050506107ff60015f55565b5050565b610886604051806101c001604052805f151581526020015f151581526020015f815260200160608152602001606081526020015f6001600160a01b03168152602001606081526020015f60ff1681526020015f81526020015f81526020015f6001600160a01b031681526020015f151581526020015f8152602001606081525090565b60025460ff90811615158252600554161515602082015267ffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001642106108d4575f6108fe565b6108fe427f000000000000000000000000000000000000000000000000000000000000000061200c565b67ffffffffffffffff1660408201526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660a0820152600180546109499061202c565b80601f01602080910402602001604051908101604052809291908181526020018280546109759061202c565b80156109c05780601f10610997576101008083540402835291602001916109c0565b820191905f5260205f20905b8154815290600101906020018083116109a357829003601f168201915b505050505060c08201527f000000000000000000000000000000000000000000000000000000000000000060ff1660e08201526003546101008201526040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610a5e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a829190612064565b61012082015260055461010081046001600160a01b0316610140830152600160a81b900460ff16151561016082015260075461018082015260068054610ac79061202c565b80601f0160208091040260200160405190810160405280929190818152602001828054610af39061202c565b8015610b3e5780601f10610b1557610100808354040283529160200191610b3e565b820191905f5260205f20905b815481529060010190602001808311610b2157829003601f168201915b50505050506101a08201526008545f819003610b7f5750604080515f8082526020808301845260608501929092528251908152908101909152608082015290565b5f8167ffffffffffffffff811115610b9957610b9961207b565b604051908082528060200260200182016040528015610bc2578160200160208202803683370190505b5090505f8267ffffffffffffffff811115610bdf57610bdf61207b565b604051908082528060200260200182016040528015610c08578160200160208202803683370190505b5090505f805b84811015610db4575f60088281548110610c2a57610c2a611ff8565b5f91825260208083206040805180820190915260029093020180546001600160a01b03168352600101549082015291508215610c9e576008610c6d600185611fe5565b81548110610c7d57610c7d611ff8565b905f5260205f209060020201600101546001610c999190611fcc565b610ca0565b5f5b60208301519091505f610cb38383611fe5565b610cbe906001611fcc565b90505f805b87811015610d3b57855f01516001600160a01b03168a8281518110610cea57610cea611ff8565b60200260200101516001600160a01b031603610d335782898281518110610d1357610d13611ff8565b60200260200101818151610d279190611fcc565b90525060019150610d3b565b600101610cc3565b5080610da357845f0151898881518110610d5757610d57611ff8565b60200260200101906001600160a01b031690816001600160a01b03168152505081888881518110610d8a57610d8a611ff8565b602090810291909101015286610d9f81611fb4565b9750505b505060019093019250610c0e915050565b505f8167ffffffffffffffff811115610dcf57610dcf61207b565b604051908082528060200260200182016040528015610df8578160200160208202803683370190505b5090505f8267ffffffffffffffff811115610e1557610e1561207b565b604051908082528060200260200182016040528015610e3e578160200160208202803683370190505b5090505f5b83811015610ed657858181518110610e5d57610e5d611ff8565b6020026020010151838281518110610e7757610e77611ff8565b60200260200101906001600160a01b031690816001600160a01b031681525050848181518110610ea957610ea9611ff8565b6020026020010151828281518110610ec357610ec3611ff8565b6020908102919091010152600101610e43565b506060870191909152608086015250929392505050565b60068054610efa9061202c565b80601f0160208091040260200160405190810160405280929190818152602001828054610f269061202c565b8015610f715780601f10610f4857610100808354040283529160200191610f71565b820191905f5260205f20905b815481529060010190602001808311610f5457829003601f168201915b505050505081565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b038116610ff55760405162461bcd60e51b815260206004820152601760248201527f456e74726f70792061646472657373206e6f742073657400000000000000000060448201526064015b60405180910390fd5b336001600160a01b038216146110595760405162461bcd60e51b815260206004820152602360248201527f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e637460448201526234b7b760e91b6064820152608401610fec565b61106484848461184e565b50505050565b60088181548110611079575f80fd5b5f918252602090912060029091020180546001909101546001600160a01b03909116915082565b6110a86117f7565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561110c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111309190612064565b60025490915060ff166111565760405163246450af60e21b815260040160405180910390fd5b60055460ff1661117957604051631a9faa4160e01b815260040160405180910390fd5b600554600160a81b900460ff16156111a45760405163611ab18960e11b815260040160405180910390fd5b60078190556040516350e32bcb60e11b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a1c65796906024015f604051808303815f87803b158015611208575f80fd5b505af115801561121a573d5f803e3d5ffd5b50506005805460ff60a81b1916600160a81b17905550506003545f919082036112f9576112916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000008361198a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f5965f39b96d8d8c1d4c61686c7eb65fe448dcef689e496837c1464029298a37d826040516112ec91815260200190565b60405180910390a261137f565b600554611338906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116916101009004168361198a565b6005546040518281526101009091046001600160a01b0316907f95681e512bc0fe659e195e06c283eada494316f3d801213e48e7101af92bf7709060200160405180910390a25b505061138a60015f55565b565b60018054610efa9061202c565b6113a16117f7565b60025460ff16156113c55760405163246450af60e21b815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff1642101561141057604051634966314360e11b815260040160405180910390fd5b6002805460ff19166001179055604080516320bba64360e21b815290515f916001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016916382ee990c916004808201926020929091908290030181865afa158015611483573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114a7919061208f565b604051631711922960e31b81526001600160a01b0380831660048301529192505f917f0000000000000000000000000000000000000000000000000000000000000000169063b88c914890602401602060405180830381865afa158015611510573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061153491906120aa565b6001600160801b03169050803410156115605760405163732f941360e01b815260040160405180910390fd5b8034111561159d57336108fc6115768334611fe5565b6040518115909202915f818181858888f1935050505015801561159b573d5f803e3d5ffd5b505b6040516319cb825f60e01b81526001600160a01b038381166004830152602482018590525f917f0000000000000000000000000000000000000000000000000000000000000000909116906319cb825f90849060440160206040518083038185885af115801561160f573d5f803e3d5ffd5b50505050506040513d601f19601f8201168201806040525081019061163491906120d0565b60405167ffffffffffffffff821681529091507fce24807f7e4b60b4e641462f13029fa5e5f79075bbc3f8e5cd43ecd19661d7609060200160405180910390a150505061168060015f55565b50565b5f805f6008848154811061169957611699611ff8565b5f9182526020918290206040805180820190915260029092020180546001600160a01b031680835260019091015491909201819052909590945092505050565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166382ee990c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611737573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061175b919061208f565b604051631711922960e31b81526001600160a01b0380831660048301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063b88c914890602401602060405180830381865afa1580156117c4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117e891906120aa565b6001600160801b031691505090565b60025f54036118485760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610fec565b60025f55565b60025460ff166118715760405163246450af60e21b815260040160405180910390fd5b60055460ff161561189557604051634a4117f960e01b815260040160405180910390fd5b6005805460ff191660011790556003545f8190036118c35760058054610100600160a81b0319169055611901565b5f6118ce82846120eb565b90506118d9816119e1565b600560016101000a8154816001600160a01b0302191690836001600160a01b03160217905550505b6005546040516101009091046001600160a01b0316907f1d4c260f1824cd028e6c9e6e31c3a0b94f2513e7a641113ec759d382f9bdd5a1905f90a260405167ffffffffffffffff851681526001600160a01b038416907fd299755aa265bbec2e80e44e14c3ae068c6505f620a5e28073bc3a0c02eca2e09060200160405180910390a250505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526119dc908490611a95565b505050565b6008545f90819081906119f690600190611fe5565b90505b808211611a7c575f6001611a0d8385611fcc565b901c90505f60088281548110611a2557611a25611ff8565b5f9182526020918290206040805180820190915260029092020180546001600160a01b031682526001015491810182905291508611611a68575195945050505050565b611a73826001611fcc565b935050506119f9565b604051633cf0cfff60e11b815260040160405180910390fd5b5f611ae9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611b669092919063ffffffff16565b8051909150156119dc5780806020019051810190611b07919061210a565b6119dc5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610fec565b6060611b7484845f85611b7c565b949350505050565b606082471015611bdd5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610fec565b5f80866001600160a01b03168587604051611bf89190612129565b5f6040518083038185875af1925050503d805f8114611c32576040519150601f19603f3d011682016040523d82523d5f602084013e611c37565b606091505b5091509150611c4887838387611c53565b979650505050505050565b60608315611cc15782515f03611cba576001600160a01b0385163b611cba5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610fec565b5081611b74565b611b748383815115611cd65781518083602001fd5b8060405162461bcd60e51b8152600401610fec9190611f02565b6001600160a01b0381168114611680575f80fd5b5f8060408385031215611d15575f80fd5b8235611d2081611cf0565b946020939093013593505050565b5f8151808452602084019350602083015f5b82811015611d675781516001600160a01b0316865260209586019590910190600101611d40565b5093949350505050565b5f8151808452602084019350602083015f5b82811015611d67578151865260209586019590910190600101611d83565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b60208152611de260208201835115159052565b5f6020830151611df6604084018215159052565b506040830151606083015260608301516101c06080840152611e1c6101e0840182611d2e565b90506080840151601f198483030160a0850152611e398282611d71565b91505060a0840151611e5660c08501826001600160a01b03169052565b5060c0840151838203601f190160e0850152611e728282611da1565b91505060e0840151611e8a61010085018260ff169052565b50610100840151610120840152610120840151610140840152610140840151611ebf6101608501826001600160a01b03169052565b50610160840151801515610180850152506101808401516101a08401526101a0840151601f19848303016101c0850152611ef98282611da1565b95945050505050565b602081525f611f146020830184611da1565b9392505050565b67ffffffffffffffff81168114611680575f80fd5b5f805f60608486031215611f42575f80fd5b8335611f4d81611f1b565b92506020840135611f5d81611cf0565b929592945050506040919091013590565b5f60208284031215611f7e575f80fd5b5035919050565b5f60208284031215611f95575f80fd5b8135611f1481611cf0565b634e487b7160e01b5f52601160045260245ffd5b5f60018201611fc557611fc5611fa0565b5060010190565b80820180821115611fdf57611fdf611fa0565b92915050565b81810381811115611fdf57611fdf611fa0565b634e487b7160e01b5f52603260045260245ffd5b67ffffffffffffffff8281168282160390811115611fdf57611fdf611fa0565b600181811c9082168061204057607f821691505b60208210810361205e57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f60208284031215612074575f80fd5b5051919050565b634e487b7160e01b5f52604160045260245ffd5b5f6020828403121561209f575f80fd5b8151611f1481611cf0565b5f602082840312156120ba575f80fd5b81516001600160801b0381168114611f14575f80fd5b5f602082840312156120e0575f80fd5b8151611f1481611f1b565b5f8261210557634e487b7160e01b5f52601260045260245ffd5b500690565b5f6020828403121561211a575f80fd5b81518015158114611f14575f80fd5b5f82518060208501845e5f92019182525091905056fea264697066735822122082003ecd4e50af55bf42bd2679a1d2a00b365c0bf737b2c1de353a78e2e43e8864736f6c634300081a0033a264697066735822122073808df5cd239849be4c7bbd66b2d0e17a6860f03d409273d7001e6f845fab1064736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000003926babb9ef6c3588a7b2c7866e74ed36702c5ac
-----Decoded View---------------
Arg [0] : _admin (address): 0x3926baBB9EF6C3588a7B2c7866E74ED36702c5Ac
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000003926babb9ef6c3588a7b2c7866e74ed36702c5ac
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 35 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ Download: CSV Export ]
[ Download: CSV Export ]
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.