Overview
S Balance
0 S
S Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 593 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Redeem | 5673780 | 5 days ago | IN | 0 S | 0.00765469 | ||||
Redeem | 5624871 | 5 days ago | IN | 0 S | 0.00197579 | ||||
Fulfill Random W... | 4664079 | 13 days ago | IN | 0 S | 0.012804 | ||||
Bet | 4664075 | 13 days ago | IN | 0.01473214 S | 0.0138731 | ||||
Fulfill Random W... | 4664066 | 13 days ago | IN | 0 S | 0.0126894 | ||||
Bet | 4664060 | 13 days ago | IN | 0.01473214 S | 0.01638016 | ||||
Fulfill Random W... | 4664007 | 13 days ago | IN | 0 S | 0.01291425 | ||||
Bet | 4664002 | 13 days ago | IN | 0.01375 S | 0.0138749 | ||||
Fulfill Random W... | 4663987 | 13 days ago | IN | 0 S | 0.0126894 | ||||
Bet | 4663985 | 13 days ago | IN | 0.01375 S | 0.0138749 | ||||
Fulfill Random W... | 4662929 | 13 days ago | IN | 0 S | 0.0109645 | ||||
Bet | 4662926 | 13 days ago | IN | 0.01375 S | 0.0138731 | ||||
Fulfill Random W... | 4662917 | 13 days ago | IN | 0 S | 0.01291305 | ||||
Bet | 4662916 | 13 days ago | IN | 0.01375 S | 0.0138731 | ||||
Fulfill Random W... | 4662910 | 13 days ago | IN | 0 S | 0.01096695 | ||||
Bet | 4662906 | 13 days ago | IN | 0.01375 S | 0.0138731 | ||||
Fulfill Random W... | 4662898 | 13 days ago | IN | 0 S | 0.0109718 | ||||
Bet | 4662895 | 13 days ago | IN | 0.01375 S | 0.0138731 | ||||
Fulfill Random W... | 4659093 | 13 days ago | IN | 0 S | 0.01280155 | ||||
Bet | 4659091 | 13 days ago | IN | 0.0322 S | 0.01598265 | ||||
Fulfill Random W... | 4659078 | 13 days ago | IN | 0 S | 0.01280155 | ||||
Bet | 4659075 | 13 days ago | IN | 0.0322 S | 0.01598265 | ||||
Fulfill Random W... | 4659037 | 13 days ago | IN | 0 S | 0.01138765 | ||||
Bet | 4659034 | 13 days ago | IN | 0.0322 S | 0.01598265 | ||||
Fulfill Random W... | 4658982 | 13 days ago | IN | 0 S | 0.01280035 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
4664075 | 13 days ago | 0.01473214 S | ||||
4664060 | 13 days ago | 0.01473214 S | ||||
4664002 | 13 days ago | 0.01375 S | ||||
4663985 | 13 days ago | 0.01375 S | ||||
4662926 | 13 days ago | 0.01375 S | ||||
4662916 | 13 days ago | 0.01375 S | ||||
4662906 | 13 days ago | 0.01375 S | ||||
4662895 | 13 days ago | 0.01375 S | ||||
4659091 | 13 days ago | 0.0322 S | ||||
4659075 | 13 days ago | 0.0322 S | ||||
4659034 | 13 days ago | 0.0322 S | ||||
4658978 | 13 days ago | 0.0322 S | ||||
4658958 | 13 days ago | 0.0322 S | ||||
4658930 | 13 days ago | 0.03492461 S | ||||
4658894 | 13 days ago | 0.03540727 S | ||||
4658833 | 13 days ago | 0.0322 S | ||||
4658818 | 13 days ago | 0.0322 S | ||||
4658797 | 13 days ago | 0.0322 S | ||||
4651341 | 13 days ago | 0.01375 S | ||||
4651317 | 13 days ago | 0.01375 S | ||||
4651293 | 13 days ago | 0.01375 S | ||||
4651275 | 13 days ago | 0.01375 S | ||||
4645153 | 13 days ago | 0.0322 S | ||||
4644276 | 13 days ago | 0.0322 S | ||||
4644184 | 13 days ago | 0.0322 S |
Loading...
Loading
Contract Name:
DiceGame
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { TransferHelper } from "./libraries/TransferHelper.sol"; contract DiceGame is Ownable { using TransferHelper for address; struct GameRound { bool fulfilled; // whether the request has been successfully fulfilled address user; uint256 totalBet; uint256 totalWinnings; uint256[] betAmts; uint256[] diceRollResult; } uint256 public constant WIN69_MULTIPLIER = 10; uint256 public constant CALLBACK_GAS = 200_000; uint256 public constant MAX_OPERATOR_GAS = 1_000_000; uint256 public constant MAX_NUM_WORDS = 3; uint256 public constant DELIMITER = 1e18; uint8 public constant decimals = 18; string public constant name = "Banana Points"; string public constant symbol = "BPT"; uint256 public rollOperatorGas = 600_000; uint256 public immutable gamePeriod; address public coin; address public immutable V3Deployer; address public wrappedNative; address public immutable gameRngWallet; address public immutable rollOperator; /// @notice Timestamp when the geme ower uint256 public endTime; /// @notice Initial rate of tokens per coin uint256 public initialTokenRate; uint256 public immutable airdropRate; uint256 public gameId; uint256 public lastFulfilledGameId; // The total supply of points in existence uint256 public totalSupply; // Maps an address to their current balance mapping(address => uint256) private userBalances; // Maps a game ID to its round information mapping(uint256 => GameRound) private gameRounds; /* gameId --> GameRound */ // Maps an address to their game IDs mapping(address => uint256[]) public userGameIds; constructor( address _gameRngWalletAddress, uint _gamePeriod, address _V3Deployer, address _rollOperator, uint256 _airdropRate ) { gameRngWallet = _gameRngWalletAddress; if (_gameRngWalletAddress == address(0) || _V3Deployer == address(0)) revert ZeroValue(); if (_gamePeriod < 2 hours || _gamePeriod > 180 days) revert GamePeriod(); gamePeriod = _gamePeriod; rollOperator = _rollOperator; V3Deployer = _V3Deployer; airdropRate = _airdropRate; transferOwnership(_V3Deployer); } event MintPoints(address recipient, uint256 pointsAmount); event BurnPoints(address from, uint256 pointsAmount); event Redeem(address user, uint256 amount); event PurchasePoints(address user, uint256 paymentAmount); event Bet(uint256 gameId, address user, uint256 totalBetAmt); event BetFailed(address user); event ClaimAirDrop(address user, uint256 amount); error AmountOfEthSentIsTooSmall(uint256 sent, uint256 minimum); error InvalidGameId(uint256 id); error InvalidLength(); error InvaliddiceRollResult(uint256 id); error GamePeriod(); error ZeroValue(); error NotEnoughCoinBalance(uint256 want, uint256 have); error Forbidden(); // Modifiers modifier shouldGameIsNotOver() { require(gameNotOver(), "game over"); _; } modifier shouldGameIsOver() { require(gameOver(), "game is NOT over"); _; } /// @notice Receive ETH and forward to `sponsorWallet`. receive() external payable { (bool success, ) = gameRngWallet.call{ value: msg.value }(""); require(success); } /** * @notice Starts a new game with specific parameters Airnode details, initial token rate, etc. * non-zero initial token rate, and game not already started (initialTokenRate == 0). * @param _initialTokenRate The initial rate used within the game logic, set at the start and never changed afterward. * @param _coin address of Coin token for this Game * @custom:modifier onlyOwner Restricts the function's execution to the contract's owner. */ function startGame( uint _initialTokenRate, address _coin, address _wrappedNative ) external payable onlyOwner { // Ensure the initial token rate is not already set require(initialTokenRate == 0, "o-o"); if (_coin == address(0)) revert ZeroValue(); // Initialize the initial token rate and calculate the end time based on the current timestamp initialTokenRate = _initialTokenRate; coin = _coin; wrappedNative = _wrappedNative; endTime = block.timestamp + gamePeriod; if (msg.value > 0) { (bool success, ) = gameRngWallet.call{ value: msg.value }(""); require(success); } } /// @notice Retrieves the balance of a given account /// @dev Returns the current balance stored in `userBalances` /// @param account The address of the user whose balance we want to retrieve /// @return The balance of the user function balanceOf(address account) public view returns (uint256) { return userBalances[account]; } /// @notice Retrieves info of particular game id /// @param _gameId game number/id /// @return gameInfo GameRound struct function getGameRoundInfo(uint256 _gameId) public view returns (GameRound memory gameInfo) { gameInfo = gameRounds[_gameId]; } /// @notice Retrieves the list of game IDs associated with a given user /// @dev Fetches the array of game IDs from `userGameIds` using `.values()` /// @param user The address of the user whose game IDs we want to retrieve /// @return ids An array of game IDs that the user participated in function getUserGameIds(address user) public view returns (uint256[] memory ids) { ids = userGameIds[user]; } /// @notice Retrieves the number of games a user has participated in /// @dev Calculates the length of the user's game IDs set /// @param user The address of the user whose number of games we want to know /// @return num The number of games the user has participated in function getUserGamesNumber(address user) public view returns (uint256 num) { num = userGameIds[user].length; } // @notice Retrieves the last game information for a given user /// @dev Fetches the last game ID and corresponding round info from `userGameIds` and `gameRounds` /// @param user The address of the user whose last game information we want to retrieve /// @return id The ID of the last game the user participated in /// @return round The GameRound struct containing the details of the game round function getUserLastGameInfo( address user ) public view returns (uint256 id, GameRound memory round) { uint256 length = userGameIds[user].length; if (length > 0) { id = userGameIds[user][length - 1]; round = gameRounds[id]; } } /// @notice Determines whether the game is still ongoing or not /// @dev Compares the current block timestamp against `endTime`; also ensures that the game has started by requiring `_endTime` to be non-zero /// @return Whether the current time is before the game's end time (`true`) or after (`false`) function gameNotOver() public view returns (bool) { uint256 _endTime = endTime; _checkZero(_endTime); return block.timestamp < _endTime; } /** * @notice Checks if the game has been concluded based on the time limit. * @dev Returns true if the current block timestamp exceeds the end time of the game by 10 minutes. * This implies a grace period of 10 minutes after the official end time before declaring the game over. * The function requires that `endTime` is set and the game has started, otherwise it reverts with an error message. * * @return A boolean value indicating whether the game is over (true) or not (false). */ function gameOver() public view returns (bool) { uint256 _endTime = endTime; _checkZero(_endTime); return (block.timestamp > _endTime && gameId == lastFulfilledGameId); } struct GameState { uint256 gameId; uint256 betNumber; } /// @dev This function returns the state of games that have not yet been fulfilled. /// It constructs an array of `GameState` structures representing each unfulfilled game's /// ID and the count of bets placed in that game round. /// The function only includes games with IDs greater than `lastFulfilledGameId`. /// @return state An array of `GameState` structs for each unfulfilled game. function getGameState() public view returns (GameState[] memory state) { if (gameId > lastFulfilledGameId) { uint256 requests = gameId - lastFulfilledGameId; state = new GameState[](requests); uint256 index; while (lastFulfilledGameId + index < gameId) { uint256 id = lastFulfilledGameId + index + 1; state[index].gameId = id; state[index].betNumber = gameRounds[id].betAmts.length; index++; } } } /// @notice Allows a user to place a bet on a dice roll(s), record the bet details, and request randomness /// @dev Transfers the required ETH to sponsor wallet and creates a new game round with provided bets /// @param _betAmts An array of amounts representing individual bets for each roll of the dice function bet(uint256[] memory _betAmts) external payable shouldGameIsNotOver { // user must send enough native for the callback // otherwise the transaction will fail uint256 minimumSend = tx.gasprice * CALLBACK_GAS; _checkAmount(minimumSend); // Transfer the received native to the gameRngWallet wallet to cover the callback transaction costs (bool success, ) = gameRngWallet.call{ value: msg.value }(""); require(success); _bet(msg.sender, _betAmts); } struct OperatorInput { address user; uint256[] betAmts; } /// @notice Allows roll operator to place batch bets for different users. Operator must add users to batch who /// have enough wrapped native tokens, have given approval for it, and have fulfilled their last game round /// @param _inputs encoded input for array of OperatorInput structs function bet(bytes calldata _inputs) external payable shouldGameIsNotOver { if (msg.sender != rollOperator) revert(); OperatorInput[] memory batchInfo = abi.decode(_inputs, (OperatorInput[])); uint length = batchInfo.length; if (length == 0 || length > 30) revert InvalidLength(); uint256 minimumSend = tx.gasprice * CALLBACK_GAS * length; uint256 gasAmt = tx.gasprice * rollOperatorGas; _checkAmount(minimumSend); OperatorInput memory info; uint successCount; for (uint i; i < length; ) { info = batchInfo[i]; try this.processBatchBet(info.user, info.betAmts) { unchecked { ++successCount; } } catch { emit BetFailed(info.user); } unchecked { ++i; } } uint refund; if (successCount > 0) { uint gasRandomizer = (msg.value * successCount) / length; refund = msg.value - gasRandomizer; (bool success, ) = gameRngWallet.call{ value: gasRandomizer }(""); require(success); (bool result, ) = wrappedNative.call( abi.encodeWithSignature( "withdrawTo(address,uint256)", msg.sender, gasAmt * successCount ) ); require(result, "withdrawTo"); } else { refund = msg.value; } if (refund > 0) { (bool success, ) = msg.sender.call{ value: refund }(""); require(success); } } function processBatchBet(address _user, uint[] memory _betAmts) external shouldGameIsNotOver { if (tx.origin != rollOperator) revert(); uint gasAmt; unchecked { gasAmt = tx.gasprice * rollOperatorGas; } wrappedNative.safeTransferFrom(_user, address(this), gasAmt); _bet(_user, _betAmts); } /// @notice Allows roll operator place bets for users /// @param _user Address of certain user /// @param _betAmts An array of amounts representing individual bets for each roll of the dice function bet(address _user, uint256[] memory _betAmts) external payable shouldGameIsNotOver { if (msg.sender != rollOperator) revert(); // msg.value must be enough to cover randomizer gas spends uint256 minimumSend = tx.gasprice * CALLBACK_GAS; // take from user gas amount*gas.price uint256 gasAmt = tx.gasprice * rollOperatorGas; _checkAmount(minimumSend); wrappedNative.safeTransferFrom(_user, address(this), gasAmt); (bool success, ) = wrappedNative.call( abi.encodeWithSignature("withdrawTo(address,uint256)", msg.sender, gasAmt) ); require(success, "withdrawTo"); // send to randomizer 200_000gas*gas.price for callback (bool result, ) = gameRngWallet.call{ value: msg.value }(""); require(result); _bet(_user, _betAmts); } function _bet(address _user, uint256[] memory betAmts) internal { (uint256 id, GameRound memory round) = getUserLastGameInfo(_user); require(round.fulfilled || id == 0, "last round not fulfilled"); // Check if the number of dice rolls is within the permitted range uint256 numWords = betAmts.length; require(numWords > 0 && numWords <= MAX_NUM_WORDS, "invalid betAmts"); // Calculate the total bet amount from the array of bets uint256 totalBetAmt; for (uint i; i < numWords; ) { // Each bet amount must be greater than zero _checkZero(betAmts[i]); unchecked { totalBetAmt += betAmts[i]; ++i; } } // Ensure the user has enough points to cover their total bet // It is possible to resend a bid for the same balance, // so this check is also added to the callback function require(totalBetAmt <= balanceOf(_user), "points are not enough"); _burnPoints(_user, totalBetAmt); unchecked { ++gameId; } uint256 _gameId = gameId; // Record the game round details in the contract state gameRounds[_gameId] = GameRound({ fulfilled: false, user: _user, totalBet: totalBetAmt, totalWinnings: 0, betAmts: betAmts, diceRollResult: new uint256[](betAmts.length) }); // Associate the game ID with the user's address userGameIds[_user].push(_gameId); emit Bet(_gameId, _user, totalBetAmt); } struct RandomData { uint256 id; uint256[] rn; } /** * @notice Fulfills the generation of random words if gas requirement is met * @dev Processes each `RandomData` entries until either all are processed or minimum remaining gas is not met * @param minRemainingGas The minimum amount of gas that must be left for the function to continue processing * @param randomData An array of `RandomData` structs containing the IDs and random number arrays to process * Requirements: * - Only callable by the `gameRngWallet`. * - Will stop processing if the remaining gas is less than `minRemainingGas`. * Emits a `RandomWordsFulfilled` event upon successful processing of an entry. * Uses the `_fulfillRandomWords` internal function to process each entry. */ function fulfillRandomWords(uint256 minRemainingGas, RandomData[] memory randomData) external { require(msg.sender == gameRngWallet, "invalid caller"); for (uint256 i; i < randomData.length; ) { if (gasleft() < minRemainingGas) { break; } _fulfillRandomWords(randomData[i].id, randomData[i].rn); unchecked { ++i; } } } /// @notice Records the result of dice rolls, updates the game round, and handles payouts /// @dev Requires the caller to be the designated AirnodeRrp address and checks if the round can be fulfilled /// @param _gameId The unique identifier of the game round that the dice roll results correspond to /// @param _randomWords The array of random numbers provided by off-chain QRNG service /// Using the QRNG service is free, meaning there is no subscription fee to pay. /// There is a gas cost incurred on-chain when Airnode places the random number on-chain in response to a request, /// which the requester needs to pay for. function _fulfillRandomWords(uint256 _gameId, uint256[] memory _randomWords) private { unchecked { ++lastFulfilledGameId; } // Retrieve the game round using the _gameId GameRound storage round = gameRounds[_gameId]; uint256 totalBet = round.totalBet; if (_gameId != lastFulfilledGameId || totalBet == 0) { revert InvalidGameId(_gameId); } uint256 length = _randomWords.length; if (length != round.diceRollResult.length) { revert InvaliddiceRollResult(_gameId); } // Mark the round as fulfilled round.fulfilled = true; uint256 totalWinnings; uint256 bitDice; bool double3; for (uint i; i < length; ) { // Get the dice number between 1 and 6 uint256 num = (_randomWords[i] % 6) + 1; // Calculate winnings based on even dice numbers if (num % 2 == 0) { totalWinnings += round.betAmts[i] * 2; } // Special logic for determining 33 if (num == 3 && !double3 && bitDice & (1 << num) == (1 << num)) { double3 = true; } bitDice |= (1 << num); round.diceRollResult[i] = num; unchecked { ++i; } } // Special logic for determining winnings if the special 69 condition is met // or if the special 666 condition is met // or if the special repdigit condition is met if (length == 3) { //Repdigit if ((bitDice & (bitDice - 1)) == 0) { totalWinnings = 0; if (bitDice == 64) { // 666 uint256 balance = balanceOf(round.user); if (balance > 0) { _burnPoints(round.user, balance); } } } else if ((bitDice == 72 && !double3) || bitDice == 112) { // 69 totalWinnings = totalBet * WIN69_MULTIPLIER; } } if (totalWinnings > 0) { round.totalWinnings = totalWinnings; _mintPoints(round.user, totalWinnings); } } /** * @notice Allows users to purchase a specified amount of points. * @param desiredAmountOut The exact amount of points the user wants to purchase. */ function purchasePoints(uint256 desiredAmountOut) external shouldGameIsNotOver { uint256 paymentAmount = calculatePaymentAmount(desiredAmountOut); coin.safeTransferFrom(msg.sender, address(this), paymentAmount); _checkZero(desiredAmountOut); _mintPoints(msg.sender, desiredAmountOut); emit PurchasePoints(msg.sender, paymentAmount); } /** * @notice Calculates the payment amount required for purchasing a specific amount of points. * @param desiredPointsAmount The desired amount of points. * @return paymentAmount The corresponding amount of payment currency that can be purchased/sold for the specified points. */ function calculatePaymentAmount( uint256 desiredPointsAmount ) public view returns (uint256 paymentAmount) { uint256 tokenRate = initialTokenRate; if (tokenRate == 0) revert ZeroValue(); uint256 intermediate = desiredPointsAmount * DELIMITER; paymentAmount = intermediate / tokenRate; // Round up only for buying if (paymentAmount == 0 || intermediate % tokenRate > 0) { paymentAmount += 1; } } /** * @notice Calculates the points amount a user receives for a given coin amount. * @param paymentAmount Amount of the payment currency (e.g., ETH) used to purchase tokens. * @return pointsAmount The resulting amount of tokens that can be purchased for the specified `paymentAmount`. */ function calculatePointsAmount( uint256 paymentAmount ) public view returns (uint256 pointsAmount) { uint256 rate = initialTokenRate; if (rate == 0) revert ZeroValue(); pointsAmount = (paymentAmount * rate) / DELIMITER; } function sendLiquidity() external shouldGameIsOver onlyOwner returns (uint amount) { amount = coin.getBalance(); coin.safeTransfer(V3Deployer, amount); } function setOperatorGas(uint256 _operatorGas) external onlyOwner { _checkZero(_operatorGas); if (_operatorGas > MAX_OPERATOR_GAS) revert(); rollOperatorGas = _operatorGas; } /// @notice Redeem points for tokens. /// @dev Burns points from the redeemer's balance and mints equivalent tokens. /// Emits a Redeem event upon success. /// Requires the game to be over. /// Requires the Token to have been set and the caller to have a non-zero point balance. /// @param signature 65 bytes signature for verify eligibility redeem tokens function redeem(bytes calldata signature) external shouldGameIsOver { _checkSignature(signature); uint256 amount = balanceOf(msg.sender); _checkZero(amount); _burnPoints(msg.sender, amount); (bool success, ) = V3Deployer.call( abi.encodeWithSignature("redeem(address,uint256)", msg.sender, amount) ); require(success); emit Redeem(msg.sender, amount); } /// @notice claimAirDrop for certain users. /// @dev Burns points from the users balance and mints equivalent coins corresponding to airdropRate. /// Emits a ClaimAirDrop event upon success. /// Requires the game to be over and distibuted /// Requires the Token to have been set and the caller to have a non-zero point balance. /// @param signature 65 bytes signature for verify eligibility redeem tokens function claimAirDrop(bytes calldata signature) external { (bool success, bytes memory response) = V3Deployer.staticcall( abi.encodeWithSignature("distributedGames(address)", address(this)) ); require(success && response.length == 32); require(abi.decode(response, (bool)), "wait for distribution"); _checkSignature(signature); uint256 points = balanceOf(msg.sender); _checkZero(points); _burnPoints(msg.sender, points); uint amount = (points * airdropRate) / DELIMITER; require(coin.getBalance() >= amount, "top up coin"); coin.safeTransfer(msg.sender, amount); emit ClaimAirDrop(msg.sender, amount); } function _checkSignature(bytes calldata signature) private view { bytes32 message = _withPrefix( keccak256(abi.encodePacked(msg.sender, block.chainid, address(this))) ); require(ECDSA.recover(message, signature) == rollOperator, "invalid signature!"); } function _withPrefix(bytes32 _hash) private pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash)); } /// @notice Mints points and assigns them to a specified account /// @dev Increments `userBalances` and `totalSupply` by the given `amount` /// @param to The address of the recipient to whom points are to be minted /// @param amount The quantity of points to be minted function _mintPoints(address to, uint256 amount) private { userBalances[to] += amount; totalSupply += amount; emit MintPoints(to, amount); } /// @notice Burns points from a specified account's balance /// @dev Decrements `userBalances` and `totalSupply` by the given `amount` /// @param from The address from which points are to be burned /// @param amount The quantity of points to be burned function _burnPoints(address from, uint256 amount) private { userBalances[from] -= amount; totalSupply -= amount; emit BurnPoints(from, amount); } function _checkZero(uint256 amount) private pure { require(amount > 0, "is zero"); } function _checkAmount(uint256 minimumSend) private { if (msg.value < minimumSend) { revert AmountOfEthSentIsTooSmall(msg.value, minimumSend); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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: MIT // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: GPL-2.0-or-later // https://github.com/Uniswap/v3-periphery/blob/main/contracts/libraries/TransferHelper.sol pragma solidity 0.8.19; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; library TransferHelper { /// @notice Transfers tokens from the targeted address to the given destination /// @notice Errors with 'STF' if transfer fails /// @param token The contract address of the token to be transferred /// @param from The originating address from which the tokens will be transferred /// @param to The destination address of the transfer /// @param value The amount to be transferred function safeTransferFrom(address token, address from, address to, uint256 value) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value) ); require(success && (data.length == 0 || abi.decode(data, (bool))), "BP-STF"); } /// @notice Transfers tokens from msg.sender to a recipient /// @dev Errors with ST if transfer fails /// @param token The contract address of the token which will be transferred /// @param to The recipient of the transfer /// @param value The value of the transfer function safeTransfer(address token, address to, uint256 value) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20.transfer.selector, to, value) ); require(success && (data.length == 0 || abi.decode(data, (bool))), "BP-ST"); } function getBalance(address token) internal view returns (uint256 balance) { bytes memory callData = abi.encodeWithSelector(IERC20.balanceOf.selector, address(this)); (bool success, bytes memory data) = token.staticcall(callData); require(success && data.length >= 32); balance = abi.decode(data, (uint256)); } function getBalanceOf(address token, address target) internal view returns (uint256 balance) { bytes memory callData = abi.encodeWithSelector(IERC20.balanceOf.selector, target); (bool success, bytes memory data) = token.staticcall(callData); require(success && data.length >= 32); balance = abi.decode(data, (uint256)); } function safeApprove(address token, address spender, uint256 amount) internal { (bool success, bytes memory data) = token.call( abi.encodeWithSelector(IERC20.approve.selector, spender, amount) ); require(success && (data.length == 0 || abi.decode(data, (bool))), "BP-SA"); } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_gameRngWalletAddress","type":"address"},{"internalType":"uint256","name":"_gamePeriod","type":"uint256"},{"internalType":"address","name":"_V3Deployer","type":"address"},{"internalType":"address","name":"_rollOperator","type":"address"},{"internalType":"uint256","name":"_airdropRate","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"sent","type":"uint256"},{"internalType":"uint256","name":"minimum","type":"uint256"}],"name":"AmountOfEthSentIsTooSmall","type":"error"},{"inputs":[],"name":"Forbidden","type":"error"},{"inputs":[],"name":"GamePeriod","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"InvalidGameId","type":"error"},{"inputs":[],"name":"InvalidLength","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"InvaliddiceRollResult","type":"error"},{"inputs":[{"internalType":"uint256","name":"want","type":"uint256"},{"internalType":"uint256","name":"have","type":"uint256"}],"name":"NotEnoughCoinBalance","type":"error"},{"inputs":[],"name":"ZeroValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"gameId","type":"uint256"},{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalBetAmt","type":"uint256"}],"name":"Bet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"}],"name":"BetFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"pointsAmount","type":"uint256"}],"name":"BurnPoints","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimAirDrop","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"pointsAmount","type":"uint256"}],"name":"MintPoints","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"paymentAmount","type":"uint256"}],"name":"PurchasePoints","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Redeem","type":"event"},{"inputs":[],"name":"CALLBACK_GAS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELIMITER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_NUM_WORDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_OPERATOR_GAS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"V3Deployer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WIN69_MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"airdropRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_inputs","type":"bytes"}],"name":"bet","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256[]","name":"_betAmts","type":"uint256[]"}],"name":"bet","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_betAmts","type":"uint256[]"}],"name":"bet","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"desiredPointsAmount","type":"uint256"}],"name":"calculatePaymentAmount","outputs":[{"internalType":"uint256","name":"paymentAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"paymentAmount","type":"uint256"}],"name":"calculatePointsAmount","outputs":[{"internalType":"uint256","name":"pointsAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"claimAirDrop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"coin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"minRemainingGas","type":"uint256"},{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256[]","name":"rn","type":"uint256[]"}],"internalType":"struct DiceGame.RandomData[]","name":"randomData","type":"tuple[]"}],"name":"fulfillRandomWords","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gameId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameNotOver","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameOver","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gamePeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameRngWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_gameId","type":"uint256"}],"name":"getGameRoundInfo","outputs":[{"components":[{"internalType":"bool","name":"fulfilled","type":"bool"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"totalBet","type":"uint256"},{"internalType":"uint256","name":"totalWinnings","type":"uint256"},{"internalType":"uint256[]","name":"betAmts","type":"uint256[]"},{"internalType":"uint256[]","name":"diceRollResult","type":"uint256[]"}],"internalType":"struct DiceGame.GameRound","name":"gameInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGameState","outputs":[{"components":[{"internalType":"uint256","name":"gameId","type":"uint256"},{"internalType":"uint256","name":"betNumber","type":"uint256"}],"internalType":"struct DiceGame.GameState[]","name":"state","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserGameIds","outputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserGamesNumber","outputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserLastGameInfo","outputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"bool","name":"fulfilled","type":"bool"},{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"totalBet","type":"uint256"},{"internalType":"uint256","name":"totalWinnings","type":"uint256"},{"internalType":"uint256[]","name":"betAmts","type":"uint256[]"},{"internalType":"uint256[]","name":"diceRollResult","type":"uint256[]"}],"internalType":"struct DiceGame.GameRound","name":"round","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialTokenRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastFulfilledGameId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256[]","name":"_betAmts","type":"uint256[]"}],"name":"processBatchBet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"desiredAmountOut","type":"uint256"}],"name":"purchasePoints","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rollOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rollOperatorGas","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sendLiquidity","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_operatorGas","type":"uint256"}],"name":"setOperatorGas","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_initialTokenRate","type":"uint256"},{"internalType":"address","name":"_coin","type":"address"},{"internalType":"address","name":"_wrappedNative","type":"address"}],"name":"startGame","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userGameIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wrappedNative","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
610120346200025c57601f62002cfe38819003918201601f1916830191906001600160401b0383118484101762000261578160a092859260409586528339810103126200025c57620000518262000277565b916020810151926200006583830162000277565b916080620000766060830162000277565b9101519462000085336200028c565b620927c060015560c08390526001600160a01b0392831615801562000251575b6200024057611c208110801562000233575b620002225760805260e0528160a05261010093845233816000541603620001df578116156200018c57620000eb906200028c565b51612a2a9182620002d483396080518281816104d80152610cc0015260a0518281816105b801528181610d7d015281816118580152611898015260c051828181602a015281816102cd0152818161050e015281816107cd01528181610bc201528181610f46015261157b015260e051828181610b01015281816113f80152818161144c0152818161197c01526124cb01525181818161063a0152610e7d0152f35b815162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608490fd5b6064835162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b845163c8e9ad0d60e01b8152600490fd5b5062ed4e008111620000b7565b8451637c946ed760e01b8152600490fd5b5082841615620000a5565b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036200025c57565b600080546001600160a01b039283166001600160a01b03198216811783559216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a356fe60806040908082526004908136101561005e575b505050361561002157600080fd5b600080808080347f00000000000000000000000000000000000000000000000000000000000000005af1610053611d26565b501561005b57005b80fd5b600092833560e01c91826306fdde03146119c557508163090594b3146119515781630a68ed3e1461192e5781630cbf20141461190f57816311df9995146118e657816318160ddd146118c757816325de6036146118835781632fc9255414611813578163313ce567146117f75781633197cbb6146117d85781633c56256b1461179a57816340d6bb821461177e578163599eac8a146114275781635d88cb4f146113e357816362bf0b9f146113ae5781636fcff4df1461139057816370a0823114611354578163715018a6146112fa5781637708d837146112d05781638538928514610ebf57816388704c7514610ea05781638891cf0d14610e655781638da5cb5b14610e3d57816395d89b4114610e025781639779ea4614610de45781639945e3d314610cff578163995f567914610ce3578163ae46631314610ca8578163b0a6330114610c11578163b4f4c1ec14610adb578163b548765f14610a91578163b7d0628b1461091e578163bdb337d1146108f8578163c1e714fe146108d0578163c2884c0a14610898578163c30cfda714610879578163c75a0103146107fc578163cbf99dd1146107b8578163d3be4fe314610762578163d7c81b5514610743578163e09af4401461057b578163e41d15b014610463578163eb6d3a111461043a578163f2fde38b14610376578163f6b0692a14610324575063fecb03b1146102685780610013565b60203660031901126103095780356001600160401b038111610320576102919036908301611b27565b906102a781546102a0816127d4565b4210611f18565b62030d4090813a02913a8304143a15171561030d57506102c69061280a565b81808080347f00000000000000000000000000000000000000000000000000000000000000005af16102f6611d26565b5015610309576103069033611fbc565b80f35b5080fd5b634e487b7160e01b845260119052602483fd5b8280fd5b839034610309576020366003190112610309576103729161035f9082906001600160a01b03610351611af5565b168152600b60205220611dad565b9051918291602083526020830190611bc7565b0390f35b9190503461032057602036600319011261032057610392611af5565b9061039b611cce565b6001600160a01b039182169283156103e857505082546001600160a01b0319811683178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b906020608492519162461bcd60e51b8352820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152fd5b83903461030957816003193601126103095760035490516001600160a01b039091168152602090f35b90506060366003190112610320576001600160a01b03906024358281169190829003610577576044359283168093036105775761049e611cce565b60055461054f578115610540575082356005556bffffffffffffffffffffffff60a01b9081600254161760025560035416176003556104fd7f000000000000000000000000000000000000000000000000000000000000000042611d56565b9055346105075780f35b80808080347f00000000000000000000000000000000000000000000000000000000000000005af1610537611d26565b501561005b5780f35b51637c946ed760e01b81528390fd5b5162461bcd60e51b815260208185015260036024820152626f2d6f60e81b6044820152606490fd5b8480fd5b9050346103205761058b36611c57565b825191858060209485810190634dffb8ed60e11b8252306024820152602481526105b481611a55565b51907f00000000000000000000000000000000000000000000000000000000000000005afa6105e1611d26565b9080610739575b15610735578084806105ff935183010191016123e9565b156106fa579061060e91612401565b33845260098152670de0b6b3a764000061065f8386205461062e816127d4565b6106388133612782565b7f000000000000000000000000000000000000000000000000000000000000000090611f50565b049260018060a01b03600254169184610677846129b7565b106106ca57857f154a25614f47446861b76a16524acf5871d8ca809bd3e9bdc00a66c759aece996106c487876106ae82338a612902565b5133815260208101919091529081906040820190565b0390a180f35b835162461bcd60e51b815291820152600b60248201526a3a37b8103ab81031b7b4b760a91b604482015260649150fd5b835162461bcd60e51b815280860184905260156024820152743bb0b4ba103337b9103234b9ba3934b13aba34b7b760591b6044820152606490fd5b8680fd5b50838151146105e8565b8390346103095781600319360112610309576020906006549051908152f35b83903461030957806003193601126103095761077c611af5565b6001600160a01b03168252600b6020528082208054602435939084101561005b57506020926107aa91611ca0565b91905490519160031b1c8152f35b839034610309578160031936011261030957517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b90503461032057602036600319011261032057816106c4916108457fb68479619550fd129cc13fd5926e396b807828988fd8d73efebc02c26a447186943592546102a0816127d4565b6106ae6108518361233b565b9261086a8460018060a01b036002541630903390612832565b610873816127d4565b33612710565b8390346103095781600319360112610309576020906001549051908152f35b8390346103095760203660031901126103095760209181906001600160a01b036108c0611af5565b168152600b845220549051908152f35b9050823461005b57602036600319011261005b57506108f16020923561233b565b9051908152f35b839034610309578160031936011261030957602090610915611ee3565b90519015158152f35b90503461032057826003193601126103205760609160065490600754808311610992575b5050815160208082528451818301819052919586958686019590830194505b83821061096e5786860387f35b84518051875283015186840152879650948501949382019360019190910190610961565b909193506109a38185969496611dfe565b906109ad82611b10565b916109ba87519384611a70565b8083526109c9601f1991611b10565b01845b818110610a6f57505081845b866109e38285611d56565b1015610a60576109f38184611d56565b90600191828101809111610a4d5780610a0c8388611f04565b515287526020600a815260038a8920015490610a288388611f04565b5101526000198114610a3a57016109d8565b634e487b7160e01b875260118652602487fd5b634e487b7160e01b885260118752602488fd5b50945050505091388080610942565b6020908851610a7d81611a09565b8781528288818301528287010152016109cc565b9050823461005b57602036600319011261005b57506005548015610acd57610ac5670de0b6b3a76400009160209435611f50565b049051908152f35b5051637c946ed760e01b8152fd5b9050610ae636611b85565b919092610af781546102a0816127d4565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081163303610c0d5762030d4091823a02923a8404143a151715610bfa5750610bbb928692838093610b5c610b566001543a611f50565b9361280a565b610b6d8382600354168b3091612832565b600354935163040b850f60e31b602082019081523360248301526044820194909452931692610ba981606481015b03601f198101835282611a70565b51925af1610bb5611d26565b50611f83565b82808080347f00000000000000000000000000000000000000000000000000000000000000005af1610beb611d26565b50156103205761030691611fbc565b634e487b7160e01b875260119052602486fd5b8580fd5b90503461032057602036600319011261032057610c918161037294610c34611d79565b5084358152600a6020522092825193610c4c85611a3a565b805460ff81161515865260081c6001600160a01b0316602086015260018101548486015260028101546060860152610c8660038201611dad565b608086015201611dad565b60a083015251918291602083526020830190611bfb565b839034610309578160031936011261030957602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b83903461030957816003193601126103095760209051600a8152f35b83903461030957610d28610d1236611c57565b90610d23610d1e611ee3565b6123aa565b612401565b33825260096020528082205490610d3e826127d4565b610d488233612782565b80516301e9a69560e41b60208201908152336024830152604482018490528491829190610d788160648101610b9b565b5190827f00000000000000000000000000000000000000000000000000000000000000005af1610da6611d26565b5015610320575133815260208101919091527f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a69080604081016106c4565b8390346103095781600319360112610309576020905162030d408152f35b839034610309578160031936011261030957805161037291610e2382611a09565b600382526210941560ea1b60208301525191829182611aac565b839034610309578160031936011261030957905490516001600160a01b039091168152602090f35b839034610309578160031936011261030957602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b8390346103095781600319360112610309576020906005549051908152f35b919050346103205781600319360112610320578035916024938435916001600160401b03908184116103205736602385011215610320578385013593610f0485611b10565b94610f1183519687611a70565b808652602093898588019260051b84010192368411610735578a8101925b84841061126f57506001600160a01b0396935050507f000000000000000000000000000000000000000000000000000000000000000085163303905061123c57809796975b855181101561123057885a1061123057610f8e8187611f04565b51519084610f9c8289611f04565b5101519160019260079184835401809355808652600a8089528787209386850154908314801590611228575b611212578351928d860190815485036111fd57508760ff19875416178655889389958a928b5b838110611117575050509e9f9e600314611037575b5050509050878161101b575b50505001969796610f74565b8282600261102f9501555460081c16612710565b38808761100f565b60001985018581116111035785166110a057505050508585911461105f575b80388080611003565b87825460081c166110838160018060a01b0316600052600960205260406000205490565b80611090575b5050611056565b61109991612782565b3880611089565b909192936048811491826110fa575b5081156110ef575b506110c3575050611056565b9080925081029181830414901517156110dd573880611089565b634e487b7160e01b855260118a528b85fd5b6070915014386110b7565b159150386110af565b5050634e487b7160e01b885260118d528e88fd5b60066111238285611f04565b5106988c8a01998a81116111e557908d809392600291829108156111a9575b5060038b14806111a1575b80611194575b61118a575b61118490838c1b179a61116b8386611ca0565b90919082549060031b91821b91600019901b1916179055565b01610fee565b9195508591611158565b50828b1b80821614611153565b50861561114d565b91928c6111bd8560039d949d809301611ca0565b9054911b1c80911b9281840414901517156111e5578d92916111de91611d56565b9838611142565b50505050508f8f60118c91634e487b7160e01b835252fd5b8f8f828d5191630b75137d60e41b8352820152fd5b885163e8302cbd60e01b8152808e018490528e90fd5b508015610fc8565b50965050505050505080f35b815162461bcd60e51b8152808701849052600e818a01526d34b73b30b634b21031b0b63632b960911b6044820152606490fd5b83358381116112cc5782018660231982360301126112cc5786519061129382611a09565b8d810135825260448101358581116112c857916112b98f928b9593869536920101611b27565b83820152815201930192610f2f565b8a80fd5b8880fd5b919050346103205782600319360112610320576020925054906112f2826127d4565b519042108152f35b833461005b578060031936011261005b57611313611cce565b80546001600160a01b03198116825581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b839034610309576020366003190112610309576020906108f1611375611af5565b6001600160a01b031660009081526009602052604090205490565b83903461030957816003193601126103095760209051620f42408152f35b50503461030957602036600319011261030957356113ca611cce565b6113d3816127d4565b620f424081116103095760015580f35b839034610309578160031936011261030957517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b83915061143336611c57565b61144185546102a0816127d4565b6001600160a01b03907f000000000000000000000000000000000000000000000000000000000000000082163303610577578201916020928382820312610c0d5781356001600160401b039283821161177a570181601f820112156107355780356114ab81611b10565b926114b888519485611a70565b818452868085019260051b8401019281841161177657878101925b848410611712575050505050805180158015611708575b6116f85762030d40803a02903a8204143a1517156116e5578161150f91999499611f50565b9160019061152761152183543a611f50565b9461280a565b606087895161153581611a09565b8b815201528894899a5b848c106116135750989950899850508315905061160757611569906115648434611f50565b611f63565b94868080806115788a34611dfe565b997f00000000000000000000000000000000000000000000000000000000000000005af16115a4611d26565b50156107355786610b9b610ba96115f0976115c784989785986003541697611f50565b945163040b850f60e31b9181019182523360248201526044810195909552939182906064820190565b806115f85750f35b81808092335af1610537611d26565b505050505050346115f0565b61161d8c84611f04565b519b8c8a8a82511691015190303b156116e1578d8d9261165d928451938492839263090594b360e01b84528a840152602496878401526044830190611bc7565b038183305af191826116bf575b50508b8a8e9f8d93156000146116b057519151911681528692917fe835ad64737fae407739c2bf764c9901751556ceb920e98995b44fa55ec87cca91a15b019b5061153f565b505050509684809101976116a8565b8482116116d057508c528b8a61166a565b634e487b7160e01b8f52604186528efd5b8d80fd5b634e487b7160e01b885260118952602488fd5b855163251f56a160e21b81528890fd5b50601e81116114ea565b83358781116117725782018a601f198286030112611772578a519061173682611a09565b8a8101358a811681036116e15782528b810135908982116116e15791611763868d80969481960101611b27565b838201528152019301926114d3565b8b80fd5b8980fd5b8780fd5b8390346103095781600319360112610309576020905160038152f35b833461005b57602036600319011261005b57506103726117c06117bb611af5565b611e0b565b83929192519384938452806020850152830190611bfb565b9190503461032057826003193601126103205760209250549051908152f35b8390346103095781600319360112610309576020905160128152f35b839034610309578160031936011261030957602090611833610d1e611ee3565b61183b611cce565b6002546001600160a01b03169061187d611854836129b7565b80937f000000000000000000000000000000000000000000000000000000000000000090612902565b51908152f35b839034610309578160031936011261030957517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b8390346103095781600319360112610309576020906008549051908152f35b83903461030957816003193601126103095760025490516001600160a01b039091168152602090f35b8390346103095781600319360112610309576020906007549051908152f35b83903461030957816003193601126103095760209051670de0b6b3a76400008152f35b5050346103095761197161196436611b85565b919092546102a0816127d4565b6001600160a01b03917f0000000000000000000000000000000000000000000000000000000000000000831632036119c1576119bc610306936001543a029060035416833091612832565b611fbc565b8380fd5b84913461032057826003193601126103205761037292506119e582611a09565b600d82526c42616e616e6120506f696e747360981b60208301525191829182611aac565b604081019081106001600160401b03821117611a2457604052565b634e487b7160e01b600052604160045260246000fd5b60c081019081106001600160401b03821117611a2457604052565b606081019081106001600160401b03821117611a2457604052565b90601f801991011681019081106001600160401b03821117611a2457604052565b6001600160401b038111611a2457601f01601f191660200190565b6020808252825181830181905290939260005b828110611ae157505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501611abf565b600435906001600160a01b0382168203611b0b57565b600080fd5b6001600160401b038111611a245760051b60200190565b81601f82011215611b0b57803591611b3e83611b10565b92611b4c6040519485611a70565b808452602092838086019260051b820101928311611b0b578301905b828210611b76575050505090565b81358152908301908301611b68565b906040600319830112611b0b576004356001600160a01b0381168103611b0b5791602435906001600160401b038211611b0b57611bc491600401611b27565b90565b90815180825260208080930193019160005b828110611be7575050505090565b835185529381019392810192600101611bd9565b611bc49181511515815260018060a01b036020830151166020820152604082015160408201526060820151606082015260a0611c46608084015160c0608085015260c0840190611bc7565b9201519060a0818403910152611bc7565b906020600319830112611b0b576004356001600160401b0392838211611b0b5780602383011215611b0b578160040135938411611b0b5760248483010111611b0b576024019190565b8054821015611cb85760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b6000546001600160a01b03163303611ce257565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b3d15611d51573d90611d3782611a91565b91611d456040519384611a70565b82523d6000602084013e565b606090565b91908201809211611d6357565b634e487b7160e01b600052601160045260246000fd5b60405190611d8682611a3a565b606060a0836000815260006020820152600060408201526000838201528260808201520152565b9060405191828154918282526020928383019160005283600020936000905b828210611de457505050611de292500383611a70565b565b855484526001958601958895509381019390910190611dcc565b91908203918211611d6357565b6000916000611e18611d79565b6001600160a01b039384168252600b6020526040822080549194919081611e40575b50505050565b919394509194506000198201918211611ecf576040611e64611ec093600493611ca0565b90549060031b1c95868152600a6020522060405193611e8285611a3a565b815460ff81161515865260081c1660208501526001810154604085015260028101546060850152611eb560038201611dad565b608085015201611dad565b60a08201529038808080611e3a565b634e487b7160e01b85526011600452602485fd5b600454611eef816127d4565b421180611ef95790565b506006546007541490565b8051821015611cb85760209160051b010190565b15611f1f57565b60405162461bcd60e51b815260206004820152600960248201526833b0b6b29037bb32b960b91b6044820152606490fd5b81810292918115918404141715611d6357565b8115611f6d570490565b634e487b7160e01b600052601260045260246000fd5b15611f8a57565b60405162461bcd60e51b815260206004820152600a6024820152697769746864726177546f60b01b6044820152606490fd5b611fc581611e0b565b511590811591612332575b50156122ed57815190811515806122e2575b156122ab5760009182905b80821061227f57505060018060a01b03811691826000526020916009835260409081600020548311612243578261202391612782565b6001938460065401938460065586519661203c88611b10565b976120498551998a611a70565b808952612058601f1991611b10565b0136838a013783519761206a89611a3a565b60008952828901848152858a0187815260608b01916000835260808c0194855260a08c0193845289600052600a8652876000209b5115159060ff8d5491610100600160a81b03905160081b169216906affffffffffffffffffffff60a81b1617178b5551898b01555160028a01556003890191519889516001600160401b0393848211611a2457680100000000000000009b8c8311611a245786908254848455808510612216575b500190600052856000208b60005b84811061220457505050505060040190518051928311611a2457898311611a2457839082548484558085106121d7575b500190600052826000208860005b8481106121c557505050505081600052600b81528260002096875490811015611a24578561116b827fca49f418dd97ad76b84ed6fb8e915ecccb519c5379cf6a4a455c2be7618fda2f9a60609a6121b795018155611ca0565b8251948552840152820152a1565b8584519401938184015501899061215e565b836000528a858460002092830192015b8281106121f5575050612150565b600081558794508c91016121e7565b88845194019381840155018c90612120565b836000528d858460002092830192015b828110612234575050612112565b600081558a94508f9101612226565b815162461bcd60e51b81526004810185905260156024820152740e0ded2dce8e640c2e4ca40dcdee840cadcdeeaced605b1b6044820152606490fd5b90926001906122976122918688611f04565b516127d4565b6122a18587611f04565b5101930190611fed565b60405162461bcd60e51b815260206004820152600f60248201526e696e76616c696420626574416d747360881b6044820152606490fd5b506003821115611fe2565b60405162461bcd60e51b815260206004820152601860248201527f6c61737420726f756e64206e6f742066756c66696c6c656400000000000000006044820152606490fd5b90501538611fd0565b9060055491821561239857670de0b6b3a764000090818102918183041490151715611d635761236a8382611f63565b92831591821561238c575b505061237d57565b9060018101809111611d635790565b06151590503880612375565b604051637c946ed760e01b8152600490fd5b156123b157565b60405162461bcd60e51b815260206004820152601060248201526f33b0b6b29034b9902727aa1037bb32b960811b6044820152606490fd5b90816020910312611b0b57518015158103611b0b5790565b604051602081013360601b81524660348301523060601b60548301526048825260808201908282106001600160401b03831117611a2457816040528251902060bc60a08401937f19457468657265756d205369676e6564204d6573736167653a0a33320000000085520152603c815261247981611a55565b51902061248583611a91565b906124936040519283611a70565b8382523684840111611b0b5760006020856124c1966124b9968387013784010152612649565b91909161252f565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169116036124f557565b60405162461bcd60e51b8152602060048201526012602482015271696e76616c6964207369676e61747572652160701b6044820152606490fd5b600581101561263357806125405750565b6001810361258d5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606490fd5b600281036125da5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606490fd5b6003146125e357565b60405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608490fd5b634e487b7160e01b600052602160045260246000fd5b90604181511460001461267757612673916020820151906060604084015193015160001a90612681565b9091565b5050600090600290565b9291907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116127045791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa156126f75781516001600160a01b038116156126f1579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b907f6ff849a3868dd59429d6eeae1c7cd02119b80ab215230e999e9b927df85644709160018060a01b03811660005260096020526040600020612754838254611d56565b905561276282600854611d56565b600855604080516001600160a01b039290921682526020820192909252a1565b907fa813766a77a805e8fc44fa2d6436a1c6a84d38d443c1238f20ed0424bcd587859160018060a01b038116600052600960205260406000206127c6838254611dfe565b905561276282600854611dfe565b156127db57565b60405162461bcd60e51b81526020600482015260076024820152666973207a65726f60c81b6044820152606490fd5b8034106128145750565b60449060405190633ce6d0ef60e21b82523460048301526024820152fd5b6040516323b872dd60e01b602082019081526001600160a01b03938416602483015292909316604484015260648084019490945292825260a0820192906001600160401b03841183851017611a24576000809493819460405251925af1612897611d26565b816128d3575b50156128a557565b60405162461bcd60e51b8152602060048201526006602482015265212816a9aa2360d11b6044820152606490fd5b80518015925082156128e8575b50503861289d565b6128fb92506020809183010191016123e9565b38806128e0565b60405163a9059cbb60e01b602082019081526001600160a01b039093166024820152604481019390935260009283929083906129418160648101610b9b565b51925af161294d611d26565b81612988575b501561295b57565b60405162461bcd60e51b815260206004820152600560248201526410940b54d560da1b6044820152606490fd5b805180159250821561299d575b505038612953565b6129b092506020809183010191016123e9565b3880612995565b60405160208101906370a0823160e01b8252306024820152602481526129dc81611a55565b6000928392839251915afa906129f0611d26565b9180612a11575b1561005b5760208280518101031261005b57506020015190565b506020825110156129f756fea164736f6c6343000813000a00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c0000000000000000000000000000000000000000000000000000000000069780000000000000000000000000a63532878168c4d8df56a85b7f0f8a1f6abb792e000000000000000000000000d7bcee2eaa9579f39c2290d48e0fa3c706c9015300000000000000000000000000000000000000000000000022b1c8c1227a0000
Deployed Bytecode
0x60806040908082526004908136101561005e575b505050361561002157600080fd5b600080808080347f00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c5af1610053611d26565b501561005b57005b80fd5b600092833560e01c91826306fdde03146119c557508163090594b3146119515781630a68ed3e1461192e5781630cbf20141461190f57816311df9995146118e657816318160ddd146118c757816325de6036146118835781632fc9255414611813578163313ce567146117f75781633197cbb6146117d85781633c56256b1461179a57816340d6bb821461177e578163599eac8a146114275781635d88cb4f146113e357816362bf0b9f146113ae5781636fcff4df1461139057816370a0823114611354578163715018a6146112fa5781637708d837146112d05781638538928514610ebf57816388704c7514610ea05781638891cf0d14610e655781638da5cb5b14610e3d57816395d89b4114610e025781639779ea4614610de45781639945e3d314610cff578163995f567914610ce3578163ae46631314610ca8578163b0a6330114610c11578163b4f4c1ec14610adb578163b548765f14610a91578163b7d0628b1461091e578163bdb337d1146108f8578163c1e714fe146108d0578163c2884c0a14610898578163c30cfda714610879578163c75a0103146107fc578163cbf99dd1146107b8578163d3be4fe314610762578163d7c81b5514610743578163e09af4401461057b578163e41d15b014610463578163eb6d3a111461043a578163f2fde38b14610376578163f6b0692a14610324575063fecb03b1146102685780610013565b60203660031901126103095780356001600160401b038111610320576102919036908301611b27565b906102a781546102a0816127d4565b4210611f18565b62030d4090813a02913a8304143a15171561030d57506102c69061280a565b81808080347f00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c5af16102f6611d26565b5015610309576103069033611fbc565b80f35b5080fd5b634e487b7160e01b845260119052602483fd5b8280fd5b839034610309576020366003190112610309576103729161035f9082906001600160a01b03610351611af5565b168152600b60205220611dad565b9051918291602083526020830190611bc7565b0390f35b9190503461032057602036600319011261032057610392611af5565b9061039b611cce565b6001600160a01b039182169283156103e857505082546001600160a01b0319811683178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b906020608492519162461bcd60e51b8352820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152fd5b83903461030957816003193601126103095760035490516001600160a01b039091168152602090f35b90506060366003190112610320576001600160a01b03906024358281169190829003610577576044359283168093036105775761049e611cce565b60055461054f578115610540575082356005556bffffffffffffffffffffffff60a01b9081600254161760025560035416176003556104fd7f000000000000000000000000000000000000000000000000000000000006978042611d56565b9055346105075780f35b80808080347f00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c5af1610537611d26565b501561005b5780f35b51637c946ed760e01b81528390fd5b5162461bcd60e51b815260208185015260036024820152626f2d6f60e81b6044820152606490fd5b8480fd5b9050346103205761058b36611c57565b825191858060209485810190634dffb8ed60e11b8252306024820152602481526105b481611a55565b51907f000000000000000000000000a63532878168c4d8df56a85b7f0f8a1f6abb792e5afa6105e1611d26565b9080610739575b15610735578084806105ff935183010191016123e9565b156106fa579061060e91612401565b33845260098152670de0b6b3a764000061065f8386205461062e816127d4565b6106388133612782565b7f00000000000000000000000000000000000000000000000022b1c8c1227a000090611f50565b049260018060a01b03600254169184610677846129b7565b106106ca57857f154a25614f47446861b76a16524acf5871d8ca809bd3e9bdc00a66c759aece996106c487876106ae82338a612902565b5133815260208101919091529081906040820190565b0390a180f35b835162461bcd60e51b815291820152600b60248201526a3a37b8103ab81031b7b4b760a91b604482015260649150fd5b835162461bcd60e51b815280860184905260156024820152743bb0b4ba103337b9103234b9ba3934b13aba34b7b760591b6044820152606490fd5b8680fd5b50838151146105e8565b8390346103095781600319360112610309576020906006549051908152f35b83903461030957806003193601126103095761077c611af5565b6001600160a01b03168252600b6020528082208054602435939084101561005b57506020926107aa91611ca0565b91905490519160031b1c8152f35b839034610309578160031936011261030957517f00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c6001600160a01b03168152602090f35b90503461032057602036600319011261032057816106c4916108457fb68479619550fd129cc13fd5926e396b807828988fd8d73efebc02c26a447186943592546102a0816127d4565b6106ae6108518361233b565b9261086a8460018060a01b036002541630903390612832565b610873816127d4565b33612710565b8390346103095781600319360112610309576020906001549051908152f35b8390346103095760203660031901126103095760209181906001600160a01b036108c0611af5565b168152600b845220549051908152f35b9050823461005b57602036600319011261005b57506108f16020923561233b565b9051908152f35b839034610309578160031936011261030957602090610915611ee3565b90519015158152f35b90503461032057826003193601126103205760609160065490600754808311610992575b5050815160208082528451818301819052919586958686019590830194505b83821061096e5786860387f35b84518051875283015186840152879650948501949382019360019190910190610961565b909193506109a38185969496611dfe565b906109ad82611b10565b916109ba87519384611a70565b8083526109c9601f1991611b10565b01845b818110610a6f57505081845b866109e38285611d56565b1015610a60576109f38184611d56565b90600191828101809111610a4d5780610a0c8388611f04565b515287526020600a815260038a8920015490610a288388611f04565b5101526000198114610a3a57016109d8565b634e487b7160e01b875260118652602487fd5b634e487b7160e01b885260118752602488fd5b50945050505091388080610942565b6020908851610a7d81611a09565b8781528288818301528287010152016109cc565b9050823461005b57602036600319011261005b57506005548015610acd57610ac5670de0b6b3a76400009160209435611f50565b049051908152f35b5051637c946ed760e01b8152fd5b9050610ae636611b85565b919092610af781546102a0816127d4565b6001600160a01b037f000000000000000000000000d7bcee2eaa9579f39c2290d48e0fa3c706c9015381163303610c0d5762030d4091823a02923a8404143a151715610bfa5750610bbb928692838093610b5c610b566001543a611f50565b9361280a565b610b6d8382600354168b3091612832565b600354935163040b850f60e31b602082019081523360248301526044820194909452931692610ba981606481015b03601f198101835282611a70565b51925af1610bb5611d26565b50611f83565b82808080347f00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c5af1610beb611d26565b50156103205761030691611fbc565b634e487b7160e01b875260119052602486fd5b8580fd5b90503461032057602036600319011261032057610c918161037294610c34611d79565b5084358152600a6020522092825193610c4c85611a3a565b805460ff81161515865260081c6001600160a01b0316602086015260018101548486015260028101546060860152610c8660038201611dad565b608086015201611dad565b60a083015251918291602083526020830190611bfb565b839034610309578160031936011261030957602090517f00000000000000000000000000000000000000000000000000000000000697808152f35b83903461030957816003193601126103095760209051600a8152f35b83903461030957610d28610d1236611c57565b90610d23610d1e611ee3565b6123aa565b612401565b33825260096020528082205490610d3e826127d4565b610d488233612782565b80516301e9a69560e41b60208201908152336024830152604482018490528491829190610d788160648101610b9b565b5190827f000000000000000000000000a63532878168c4d8df56a85b7f0f8a1f6abb792e5af1610da6611d26565b5015610320575133815260208101919091527f222838db2794d11532d940e8dec38ae307ed0b63cd97c233322e221f998767a69080604081016106c4565b8390346103095781600319360112610309576020905162030d408152f35b839034610309578160031936011261030957805161037291610e2382611a09565b600382526210941560ea1b60208301525191829182611aac565b839034610309578160031936011261030957905490516001600160a01b039091168152602090f35b839034610309578160031936011261030957602090517f00000000000000000000000000000000000000000000000022b1c8c1227a00008152f35b8390346103095781600319360112610309576020906005549051908152f35b919050346103205781600319360112610320578035916024938435916001600160401b03908184116103205736602385011215610320578385013593610f0485611b10565b94610f1183519687611a70565b808652602093898588019260051b84010192368411610735578a8101925b84841061126f57506001600160a01b0396935050507f00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c85163303905061123c57809796975b855181101561123057885a1061123057610f8e8187611f04565b51519084610f9c8289611f04565b5101519160019260079184835401809355808652600a8089528787209386850154908314801590611228575b611212578351928d860190815485036111fd57508760ff19875416178655889389958a928b5b838110611117575050509e9f9e600314611037575b5050509050878161101b575b50505001969796610f74565b8282600261102f9501555460081c16612710565b38808761100f565b60001985018581116111035785166110a057505050508585911461105f575b80388080611003565b87825460081c166110838160018060a01b0316600052600960205260406000205490565b80611090575b5050611056565b61109991612782565b3880611089565b909192936048811491826110fa575b5081156110ef575b506110c3575050611056565b9080925081029181830414901517156110dd573880611089565b634e487b7160e01b855260118a528b85fd5b6070915014386110b7565b159150386110af565b5050634e487b7160e01b885260118d528e88fd5b60066111238285611f04565b5106988c8a01998a81116111e557908d809392600291829108156111a9575b5060038b14806111a1575b80611194575b61118a575b61118490838c1b179a61116b8386611ca0565b90919082549060031b91821b91600019901b1916179055565b01610fee565b9195508591611158565b50828b1b80821614611153565b50861561114d565b91928c6111bd8560039d949d809301611ca0565b9054911b1c80911b9281840414901517156111e5578d92916111de91611d56565b9838611142565b50505050508f8f60118c91634e487b7160e01b835252fd5b8f8f828d5191630b75137d60e41b8352820152fd5b885163e8302cbd60e01b8152808e018490528e90fd5b508015610fc8565b50965050505050505080f35b815162461bcd60e51b8152808701849052600e818a01526d34b73b30b634b21031b0b63632b960911b6044820152606490fd5b83358381116112cc5782018660231982360301126112cc5786519061129382611a09565b8d810135825260448101358581116112c857916112b98f928b9593869536920101611b27565b83820152815201930192610f2f565b8a80fd5b8880fd5b919050346103205782600319360112610320576020925054906112f2826127d4565b519042108152f35b833461005b578060031936011261005b57611313611cce565b80546001600160a01b03198116825581906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b839034610309576020366003190112610309576020906108f1611375611af5565b6001600160a01b031660009081526009602052604090205490565b83903461030957816003193601126103095760209051620f42408152f35b50503461030957602036600319011261030957356113ca611cce565b6113d3816127d4565b620f424081116103095760015580f35b839034610309578160031936011261030957517f000000000000000000000000d7bcee2eaa9579f39c2290d48e0fa3c706c901536001600160a01b03168152602090f35b83915061143336611c57565b61144185546102a0816127d4565b6001600160a01b03907f000000000000000000000000d7bcee2eaa9579f39c2290d48e0fa3c706c9015382163303610577578201916020928382820312610c0d5781356001600160401b039283821161177a570181601f820112156107355780356114ab81611b10565b926114b888519485611a70565b818452868085019260051b8401019281841161177657878101925b848410611712575050505050805180158015611708575b6116f85762030d40803a02903a8204143a1517156116e5578161150f91999499611f50565b9160019061152761152183543a611f50565b9461280a565b606087895161153581611a09565b8b815201528894899a5b848c106116135750989950899850508315905061160757611569906115648434611f50565b611f63565b94868080806115788a34611dfe565b997f00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c5af16115a4611d26565b50156107355786610b9b610ba96115f0976115c784989785986003541697611f50565b945163040b850f60e31b9181019182523360248201526044810195909552939182906064820190565b806115f85750f35b81808092335af1610537611d26565b505050505050346115f0565b61161d8c84611f04565b519b8c8a8a82511691015190303b156116e1578d8d9261165d928451938492839263090594b360e01b84528a840152602496878401526044830190611bc7565b038183305af191826116bf575b50508b8a8e9f8d93156000146116b057519151911681528692917fe835ad64737fae407739c2bf764c9901751556ceb920e98995b44fa55ec87cca91a15b019b5061153f565b505050509684809101976116a8565b8482116116d057508c528b8a61166a565b634e487b7160e01b8f52604186528efd5b8d80fd5b634e487b7160e01b885260118952602488fd5b855163251f56a160e21b81528890fd5b50601e81116114ea565b83358781116117725782018a601f198286030112611772578a519061173682611a09565b8a8101358a811681036116e15782528b810135908982116116e15791611763868d80969481960101611b27565b838201528152019301926114d3565b8b80fd5b8980fd5b8780fd5b8390346103095781600319360112610309576020905160038152f35b833461005b57602036600319011261005b57506103726117c06117bb611af5565b611e0b565b83929192519384938452806020850152830190611bfb565b9190503461032057826003193601126103205760209250549051908152f35b8390346103095781600319360112610309576020905160128152f35b839034610309578160031936011261030957602090611833610d1e611ee3565b61183b611cce565b6002546001600160a01b03169061187d611854836129b7565b80937f000000000000000000000000a63532878168c4d8df56a85b7f0f8a1f6abb792e90612902565b51908152f35b839034610309578160031936011261030957517f000000000000000000000000a63532878168c4d8df56a85b7f0f8a1f6abb792e6001600160a01b03168152602090f35b8390346103095781600319360112610309576020906008549051908152f35b83903461030957816003193601126103095760025490516001600160a01b039091168152602090f35b8390346103095781600319360112610309576020906007549051908152f35b83903461030957816003193601126103095760209051670de0b6b3a76400008152f35b5050346103095761197161196436611b85565b919092546102a0816127d4565b6001600160a01b03917f000000000000000000000000d7bcee2eaa9579f39c2290d48e0fa3c706c90153831632036119c1576119bc610306936001543a029060035416833091612832565b611fbc565b8380fd5b84913461032057826003193601126103205761037292506119e582611a09565b600d82526c42616e616e6120506f696e747360981b60208301525191829182611aac565b604081019081106001600160401b03821117611a2457604052565b634e487b7160e01b600052604160045260246000fd5b60c081019081106001600160401b03821117611a2457604052565b606081019081106001600160401b03821117611a2457604052565b90601f801991011681019081106001600160401b03821117611a2457604052565b6001600160401b038111611a2457601f01601f191660200190565b6020808252825181830181905290939260005b828110611ae157505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501611abf565b600435906001600160a01b0382168203611b0b57565b600080fd5b6001600160401b038111611a245760051b60200190565b81601f82011215611b0b57803591611b3e83611b10565b92611b4c6040519485611a70565b808452602092838086019260051b820101928311611b0b578301905b828210611b76575050505090565b81358152908301908301611b68565b906040600319830112611b0b576004356001600160a01b0381168103611b0b5791602435906001600160401b038211611b0b57611bc491600401611b27565b90565b90815180825260208080930193019160005b828110611be7575050505090565b835185529381019392810192600101611bd9565b611bc49181511515815260018060a01b036020830151166020820152604082015160408201526060820151606082015260a0611c46608084015160c0608085015260c0840190611bc7565b9201519060a0818403910152611bc7565b906020600319830112611b0b576004356001600160401b0392838211611b0b5780602383011215611b0b578160040135938411611b0b5760248483010111611b0b576024019190565b8054821015611cb85760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b6000546001600160a01b03163303611ce257565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b3d15611d51573d90611d3782611a91565b91611d456040519384611a70565b82523d6000602084013e565b606090565b91908201809211611d6357565b634e487b7160e01b600052601160045260246000fd5b60405190611d8682611a3a565b606060a0836000815260006020820152600060408201526000838201528260808201520152565b9060405191828154918282526020928383019160005283600020936000905b828210611de457505050611de292500383611a70565b565b855484526001958601958895509381019390910190611dcc565b91908203918211611d6357565b6000916000611e18611d79565b6001600160a01b039384168252600b6020526040822080549194919081611e40575b50505050565b919394509194506000198201918211611ecf576040611e64611ec093600493611ca0565b90549060031b1c95868152600a6020522060405193611e8285611a3a565b815460ff81161515865260081c1660208501526001810154604085015260028101546060850152611eb560038201611dad565b608085015201611dad565b60a08201529038808080611e3a565b634e487b7160e01b85526011600452602485fd5b600454611eef816127d4565b421180611ef95790565b506006546007541490565b8051821015611cb85760209160051b010190565b15611f1f57565b60405162461bcd60e51b815260206004820152600960248201526833b0b6b29037bb32b960b91b6044820152606490fd5b81810292918115918404141715611d6357565b8115611f6d570490565b634e487b7160e01b600052601260045260246000fd5b15611f8a57565b60405162461bcd60e51b815260206004820152600a6024820152697769746864726177546f60b01b6044820152606490fd5b611fc581611e0b565b511590811591612332575b50156122ed57815190811515806122e2575b156122ab5760009182905b80821061227f57505060018060a01b03811691826000526020916009835260409081600020548311612243578261202391612782565b6001938460065401938460065586519661203c88611b10565b976120498551998a611a70565b808952612058601f1991611b10565b0136838a013783519761206a89611a3a565b60008952828901848152858a0187815260608b01916000835260808c0194855260a08c0193845289600052600a8652876000209b5115159060ff8d5491610100600160a81b03905160081b169216906affffffffffffffffffffff60a81b1617178b5551898b01555160028a01556003890191519889516001600160401b0393848211611a2457680100000000000000009b8c8311611a245786908254848455808510612216575b500190600052856000208b60005b84811061220457505050505060040190518051928311611a2457898311611a2457839082548484558085106121d7575b500190600052826000208860005b8481106121c557505050505081600052600b81528260002096875490811015611a24578561116b827fca49f418dd97ad76b84ed6fb8e915ecccb519c5379cf6a4a455c2be7618fda2f9a60609a6121b795018155611ca0565b8251948552840152820152a1565b8584519401938184015501899061215e565b836000528a858460002092830192015b8281106121f5575050612150565b600081558794508c91016121e7565b88845194019381840155018c90612120565b836000528d858460002092830192015b828110612234575050612112565b600081558a94508f9101612226565b815162461bcd60e51b81526004810185905260156024820152740e0ded2dce8e640c2e4ca40dcdee840cadcdeeaced605b1b6044820152606490fd5b90926001906122976122918688611f04565b516127d4565b6122a18587611f04565b5101930190611fed565b60405162461bcd60e51b815260206004820152600f60248201526e696e76616c696420626574416d747360881b6044820152606490fd5b506003821115611fe2565b60405162461bcd60e51b815260206004820152601860248201527f6c61737420726f756e64206e6f742066756c66696c6c656400000000000000006044820152606490fd5b90501538611fd0565b9060055491821561239857670de0b6b3a764000090818102918183041490151715611d635761236a8382611f63565b92831591821561238c575b505061237d57565b9060018101809111611d635790565b06151590503880612375565b604051637c946ed760e01b8152600490fd5b156123b157565b60405162461bcd60e51b815260206004820152601060248201526f33b0b6b29034b9902727aa1037bb32b960811b6044820152606490fd5b90816020910312611b0b57518015158103611b0b5790565b604051602081013360601b81524660348301523060601b60548301526048825260808201908282106001600160401b03831117611a2457816040528251902060bc60a08401937f19457468657265756d205369676e6564204d6573736167653a0a33320000000085520152603c815261247981611a55565b51902061248583611a91565b906124936040519283611a70565b8382523684840111611b0b5760006020856124c1966124b9968387013784010152612649565b91909161252f565b6001600160a01b037f000000000000000000000000d7bcee2eaa9579f39c2290d48e0fa3c706c9015381169116036124f557565b60405162461bcd60e51b8152602060048201526012602482015271696e76616c6964207369676e61747572652160701b6044820152606490fd5b600581101561263357806125405750565b6001810361258d5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606490fd5b600281036125da5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606490fd5b6003146125e357565b60405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608490fd5b634e487b7160e01b600052602160045260246000fd5b90604181511460001461267757612673916020820151906060604084015193015160001a90612681565b9091565b5050600090600290565b9291907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083116127045791608094939160ff602094604051948552168484015260408301526060820152600093849182805260015afa156126f75781516001600160a01b038116156126f1579190565b50600190565b50604051903d90823e3d90fd5b50505050600090600390565b907f6ff849a3868dd59429d6eeae1c7cd02119b80ab215230e999e9b927df85644709160018060a01b03811660005260096020526040600020612754838254611d56565b905561276282600854611d56565b600855604080516001600160a01b039290921682526020820192909252a1565b907fa813766a77a805e8fc44fa2d6436a1c6a84d38d443c1238f20ed0424bcd587859160018060a01b038116600052600960205260406000206127c6838254611dfe565b905561276282600854611dfe565b156127db57565b60405162461bcd60e51b81526020600482015260076024820152666973207a65726f60c81b6044820152606490fd5b8034106128145750565b60449060405190633ce6d0ef60e21b82523460048301526024820152fd5b6040516323b872dd60e01b602082019081526001600160a01b03938416602483015292909316604484015260648084019490945292825260a0820192906001600160401b03841183851017611a24576000809493819460405251925af1612897611d26565b816128d3575b50156128a557565b60405162461bcd60e51b8152602060048201526006602482015265212816a9aa2360d11b6044820152606490fd5b80518015925082156128e8575b50503861289d565b6128fb92506020809183010191016123e9565b38806128e0565b60405163a9059cbb60e01b602082019081526001600160a01b039093166024820152604481019390935260009283929083906129418160648101610b9b565b51925af161294d611d26565b81612988575b501561295b57565b60405162461bcd60e51b815260206004820152600560248201526410940b54d560da1b6044820152606490fd5b805180159250821561299d575b505038612953565b6129b092506020809183010191016123e9565b3880612995565b60405160208101906370a0823160e01b8252306024820152602481526129dc81611a55565b6000928392839251915afa906129f0611d26565b9180612a11575b1561005b5760208280518101031261005b57506020015190565b506020825110156129f756fea164736f6c6343000813000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c0000000000000000000000000000000000000000000000000000000000069780000000000000000000000000a63532878168c4d8df56a85b7f0f8a1f6abb792e000000000000000000000000d7bcee2eaa9579f39c2290d48e0fa3c706c9015300000000000000000000000000000000000000000000000022b1c8c1227a0000
-----Decoded View---------------
Arg [0] : _gameRngWalletAddress (address): 0x63795e0f9223Ec4BFeF5fBE3dbf9331F1C57cC5c
Arg [1] : _gamePeriod (uint256): 432000
Arg [2] : _V3Deployer (address): 0xA63532878168C4D8Df56A85b7F0f8a1F6ABB792E
Arg [3] : _rollOperator (address): 0xD7BCEE2EaA9579F39c2290d48e0fA3c706C90153
Arg [4] : _airdropRate (uint256): 2500000000000000000
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000063795e0f9223ec4bfef5fbe3dbf9331f1c57cc5c
Arg [1] : 0000000000000000000000000000000000000000000000000000000000069780
Arg [2] : 000000000000000000000000a63532878168c4d8df56a85b7f0f8a1f6abb792e
Arg [3] : 000000000000000000000000d7bcee2eaa9579f39c2290d48e0fa3c706c90153
Arg [4] : 00000000000000000000000000000000000000000000000022b1c8c1227a0000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 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.