Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
VerticalAIRaffle
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.28; // OpenZeppelin Upgradeable Contracts import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // Chainlink VRF Contracts import "chainlink/contracts/src/v0.8/vrf/dev/VRFV2PlusWrapperConsumerBase.sol"; import "chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol"; /** * @title VerticalAIRaffle * @notice A raffle system for VerticalAI with upgradeability, Chainlink VRF randomness, and time-based ticket multipliers. * @dev This contract uses the Transparent Proxy Pattern (no constructor, uses `initialize`). * It allows an admin/owner to start raffles, configure parameters, and distribute prizes. * Users can buy tickets, which receive a weight multiplier depending on the week. * Random winners are selected using Chainlink VRF, and users can unstake their tokens at any time. * If a user does not unstake their tokens before the next raffle starts, their staked tokens carry over into the new raffle phase, * receiving the highest multiplier again. */ contract VerticalAIRaffle is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable, VRFV2PlusWrapperConsumerBase(0x02aae1A04f9828517b3007f83f6181900CaD910c) { using SafeERC20 for IERC20; /// @notice Emitted when a randomness request is sent. event RequestSent(uint256 indexed requestId, uint32 numWords); /// @notice Emitted when a randomness request is fulfilled. event RequestFulfilled( uint256 indexed requestId, uint256[] randomWords, uint256 payment ); /// @notice Emitted when a new raffle starts. event RaffleStarted( uint256 ticketPrice, uint256 prizePool, uint256[] prizeDistribution, uint256 raffleEndTime ); /// @notice Emitted when a user buys tickets. event TicketsBought(address indexed buyer, uint256 amount); /// @notice Emitted when a user claims their prize. event PrizeClaimed(address indexed winner, uint256 amount); /// @notice Emitted when a user unstakes their tickets. event TicketsUnstaked( address indexed user, uint256 amount, uint256 refundAmount ); /** * @notice Emitted when raffle configuration is updated. */ event RaffleConfigurationUpdated( uint256 prizePool, uint256[] prizeDistribution ); /// @notice Emitted when the admin withdraws unclaimed funds. event UnclaimedFundsWithdrawn(address indexed admin, uint256 amount); /// @notice Emitted when the admin performs an emergency token withdrawal. event EmergencyWithdraw( address indexed owner, address indexed tokenAddress, uint256 amount ); /// @notice Emitted when the admin performs an emergency ETH withdrawal. event EmergencyWithdrawETH(address indexed owner, uint256 amount); /// @notice Emitted when the staking token is changed. event StakingTokenChanged(address indexed newStakingToken); /// @notice Emitted when the VRF settings are changed. event VRFSettingsChanged( uint256 callbackGasLimit, uint16 requestConfirmations ); // ------------------------------------------------------- // Storage Layout // IMPORTANT: Do not modify existing variables' order or types. // For upgrades, append new variables at the end only. // ------------------------------------------------------- /// @notice The ERC20 token used for buying (staking) tickets. Users stake these tokens when buying tickets. IERC20 public stakingToken; /// @notice The USDC ERC20 token used for prize distribution. IERC20 public usdcToken; /// @notice The timestamp at which the current raffle starts. uint256 public raffleStartTime; /// @notice The timestamp at which the current raffle ends. uint256 public raffleEndTime; /// @notice The timestamp after which ticket purchases (staking) are disabled. Typically 72 hours before raffle end. uint256 public stakingDisabledTime; /// @notice The price per ticket, denominated in `stakingToken` units. uint256 public ticketPrice; /// @notice Total number of raw tickets sold in the current raffle. uint256 public totalTickets; /// @notice Total prize pool in USDC for the current raffle phase. uint256 public prizePool; /// @notice The distribution percentages of the prize pool. For example: [50, 25, 10, 10, 5]. uint256[] public prizeDistribution; /// @notice Indicates if prizes have already been distributed for the ended raffle. bool public prizesDistributed; /** * @dev Struct to represent the stakes of a user. * @param tokens The amount of tokens staked by the user. * @param timeStaked The timestamp when the tokens were staked. */ struct Stakes { uint256 tokens; uint256 timeStaked; } /// @dev Data structure to hold a user's ticket information. struct User { Stakes[] stakes; // Array of stakes made by the user uint256 tickets; // Number of tickets incl. multipliers uint256 bonus; // Bonus tickets for the user bool hasClaimed; // True if the user has claimed their prize } /// @dev Data structure to hold the status of a VRF request. struct RequestStatus { uint256 paid; // amount paid in link bool fulfilled; // whether the request has been successfully fulfilled uint256[] randomWords; } /// @notice Mapping of users to their raffle data. mapping(address => User) public users; /// @notice List of all participants in the current raffle. address[] public participants; /// @notice Array of winner addresses selected after raffle ends. address[] public winners; /// @notice Mapping of winners to their assigned prize amount in USDC. mapping(address => uint256) public winnerPrize; /// @notice Mapping of VRF request IDs to their status. mapping(uint256 => RequestStatus) public s_requests; // Track selected winners to ensure uniqueness mapping(address => bool) public selectedWinners; /// @notice Timestamp marking when the last raffle ended. Used for unclaimed prize withdrawal logic. uint256 public lastRaffleEndTimestamp; /// @notice Array of past VRF request IDs. uint256[] public requestIds; /// @notice The last VRF request ID. uint256 public lastRequestId; /// @notice Indicates if randomness has been requested for the current raffle. bool public randomRequested; /// @notice Indicates if the requested randomness has been fulfilled by the VRF Coordinator. bool public randomFulfilled; /// @notice Gas limit for the callback function. uint32 public callbackGasLimit; /// @notice Number of confirmations the VRF request should wait before fulfillment. uint16 public requestConfirmations; /// @notice Number of random words to request from the VRF. uint32 public numWords; // Constants /// @notice The fixed duration of a raffle: 3 weeks. uint256 public constant RAFFLE_DURATION = 21 days; /// @notice The time before raffle end when staking (ticket purchases) is disabled: 72 hours. uint256 public constant DISABLE_STAKING_BEFORE_END = 72 hours; /// @notice The delay after which unclaimed prize funds can be withdrawn by the admin: 7 days. uint256 public constant UNCLAIMED_WITHDRAW_DELAY = 7 days; /// @notice The multiplier applied to ticket weight in the first week of the raffle. uint32 public constant MULTIPLIER_ON_FRESH_RAFFLE = 3; // ------------------------------------------------------- // Initialization (instead of constructor) // ------------------------------------------------------- /** * @notice Initializes the raffle contract. This replaces the constructor for upgradeability. * @dev Can only be called once. * @param _owner The address of the contract owner. * @param _stakingToken The ERC20 token used for staking (buying tickets). * @param _usdcToken The ERC20 token (USDC) used for prize distribution. */ function initialize( address _owner, address _stakingToken, address _usdcToken ) external initializer { // Check if the owner address is valid require(_owner != address(0), "VerticalAIRaffle: Invalid owner"); // Check if the staking token address is valid require( _stakingToken != address(0), "VerticalAIRaffle: Invalid staking token" ); // Check if the USDC token address is valid require( _usdcToken != address(0), "VerticalAIRaffle: Invalid USDC token" ); // Initialize the Ownable module with the owner address __Ownable_init(_owner); // Initialize the ReentrancyGuard module __ReentrancyGuard_init(); // Set the staking token address stakingToken = IERC20(_stakingToken); // Set the USDC token address usdcToken = IERC20(_usdcToken); // Initialize VRFV2PlusWrapperConsumerBase with the wrapper address callbackGasLimit = 800_000; requestConfirmations = 3; } // ------------------------------------------------------- // Raffle Management (Admin) // ------------------------------------------------------- /** * @notice Starts a new raffle phase. Only callable by the owner. * @dev * - Resets and prepares the state for the new raffle. * - If users did not unstake their tokens after the last raffle, their staked tickets are carried over into this new raffle at the highest multiplier (3x). * @param _ticketPrice The new ticket price. * @param _prizePool The new prize pool in USDC. * @param _prizeDistribution The prize distribution percentages for the new raffle. */ function startNewRaffle( uint256 _ticketPrice, uint256 _prizePool, uint256[] memory _prizeDistribution ) external onlyOwner { require(!raffleActive(), "VerticalAIRaffle: Previous raffle not ended"); // Check if we are within the unclaimed withdraw delay require( block.timestamp > lastRaffleEndTimestamp + UNCLAIMED_WITHDRAW_DELAY, "VerticalAIRaffle: Unclaimed funds still locked" ); require( _ticketPrice >= 1 ether, "VerticalAIRaffle: Invalid ticket price - must be at least 1 VertAI token" ); require( _prizePool >= 1_000_000, "VerticalAIRaffle: Prize pool must be at least 1 USDC" ); require( _prizeDistribution.length > 0, "VerticalAIRaffle: Invalid prize distribution" ); require( _sumPrizeDistribution(_prizeDistribution) == 100, "VerticalAIRaffle: Prize distribution must be exactly 100%" ); _resetRaffleState(); ticketPrice = _ticketPrice; prizePool = _prizePool; prizeDistribution = _prizeDistribution; raffleStartTime = block.timestamp; raffleEndTime = block.timestamp + RAFFLE_DURATION; stakingDisabledTime = raffleEndTime - DISABLE_STAKING_BEFORE_END; prizesDistributed = false; randomRequested = false; randomFulfilled = false; emit RaffleStarted( _ticketPrice, _prizePool, _prizeDistribution, raffleEndTime ); } /** * @dev Internal function to reset the state of the raffle before starting a new one. * Users who did not unstake their tokens are carried over into the new raffle. * Their bonus is recalculated using the highest multiplier (3x). */ function _resetRaffleState() internal { address[] memory oldParticipants = participants; totalTickets = 0; // Clear previous winners from the mapping for (uint256 i = 0; i < winners.length; i++) { delete selectedWinners[winners[i]]; } // Clear arrays delete participants; delete winners; for (uint256 i = 0; i < oldParticipants.length; i++) { address userAddr = oldParticipants[i]; User storage u = users[userAddr]; if (u.tickets > 0) { // User still has staked tickets, carry them over with highest multiplier u.bonus = (u.tickets * MULTIPLIER_ON_FRESH_RAFFLE) - u.tickets; // Reset hasClaimed for the new raffle u.hasClaimed = false; // Add user back as participant in new raffle participants.push(userAddr); totalTickets += (u.tickets + u.bonus); } else { // Explicitly reset each field instead of using delete u.tickets = 0; u.bonus = 0; u.hasClaimed = false; // Clear stakes array safely while (u.stakes.length > 0) { u.stakes.pop(); } } } } /** * @notice Sets the raffle configuration mid-round if needed. Only owner. * @dev Allows updating the prize pool and distribution if the raffle is active. * @param _prizePool The updated prize pool in USDC. * @param _prizeDistribution The updated prize distribution percentages. */ function setRaffleConfiguration( uint256 _prizePool, uint256[] memory _prizeDistribution ) external onlyOwner { require(raffleActive(), "VerticalAIRaffle: No active raffle"); require( _prizeDistribution.length > 0, "VerticalAIRaffle: Invalid prize distribution" ); require( _sumPrizeDistribution(_prizeDistribution) == 100, "VerticalAIRaffle: Prize distribution must be exactly 100%" ); prizePool = _prizePool; prizeDistribution = _prizeDistribution; emit RaffleConfigurationUpdated(_prizePool, _prizeDistribution); } // ------------------------------------------------------- // Buying Tickets (User) // ------------------------------------------------------- /** * @notice Allows users to buy tickets using the stakingToken. Must be approved beforehand. * @dev Transfers staking tokens from the user to the contract, calculates tickets and bonus based on current multiplier, * updates user's stakes and total tickets accordingly. * @param amount The amount of staking tokens to spend for buying tickets. */ function buyTickets(uint256 amount) external nonReentrant { require(raffleActive(), "VerticalAIRaffle: Staking not active"); require( block.timestamp < stakingDisabledTime, "VerticalAIRaffle: Staking disabled" ); require(amount > 0, "VerticalAIRaffle: Amount must be > 0"); require(amount % ticketPrice == 0, "VerticalAIRaffle: Invalid amount"); uint256 tickets = amount / ticketPrice; // Check if the user has enough allowance uint256 allowance = stakingToken.allowance(msg.sender, address(this)); require( allowance >= amount, "VerticalAIRaffle: Insufficient allowance. Please approve the contract to spend your tokens." ); stakingToken.safeTransferFrom(msg.sender, address(this), amount); uint256 multiplier = getMultiplier(); User storage u = users[msg.sender]; if (u.tickets == 0) { participants.push(msg.sender); } // Calculate new tickets and bonus for this purchase only uint256 newTickets = tickets; uint256 newBonus = tickets * (multiplier - 1); // Update user's total tickets and bonus u.tickets += newTickets; u.bonus += newBonus; // Add a new stake for the user u.stakes.push(Stakes({tokens: amount, timeStaked: block.timestamp})); // Update total tickets with only the new tickets and bonus totalTickets += (newTickets + newBonus); emit TicketsBought(msg.sender, tickets); } /** * @notice Returns the current week-based multiplier for ticket weight. * @dev Week 1: 3x, Week 2: 2x, Week 3: 1x. Returns 0 if raffle is not active. * @return The current multiplier for purchased tickets. */ function getMultiplier() public view returns (uint256) { if (!raffleActive()) { return 0; } uint256 elapsed = block.timestamp > raffleEndTime ? RAFFLE_DURATION : block.timestamp - (raffleEndTime - RAFFLE_DURATION); if (elapsed < 7 days) { return 3; // Week 1 } else if (elapsed < 14 days) { return 2; // Week 2 } else { return 1; // Week 3 } } // ------------------------------------------------------- // Distributing Prizes (Admin) // ------------------------------------------------------- /** * @notice Called by the owner after the raffle ends to request randomness for selecting winners. * @dev Uses Chainlink VRF to request random words. The number of words requested is based on the length of the prize distribution. * @param enableNativePayment Determines if the payment for VRF is made in native tokens. * @return requestId The ID of the randomness request. */ function distributePrizes( bool enableNativePayment ) external payable onlyOwner nonReentrant returns (uint256 requestId) { require(!raffleActive(), "VerticalAIRaffle: Raffle still active"); require( !prizesDistributed, "VerticalAIRaffle: Prizes already distributed" ); require( !randomRequested, "VerticalAIRaffle: Randomness already requested" ); // Dynamically set numWords based on prizeDistribution length numWords = uint32(prizeDistribution.length); bytes memory extraArgs = VRFV2PlusClient._argsToBytes( VRFV2PlusClient.ExtraArgsV1({nativePayment: enableNativePayment}) ); uint256 reqPrice; if (enableNativePayment) { (requestId, reqPrice) = requestRandomnessPayInNative( callbackGasLimit, requestConfirmations, numWords, extraArgs ); } else { (requestId, reqPrice) = requestRandomness( callbackGasLimit, requestConfirmations, numWords, extraArgs ); } s_requests[requestId] = RequestStatus({ paid: reqPrice, randomWords: new uint256[](0), fulfilled: false }); requestIds.push(requestId); lastRequestId = requestId; randomRequested = true; emit RequestSent(requestId, numWords); } /** * @notice VRF callback function to fulfill randomness. * @dev Called by the VRF Coordinator once random words are ready. Finalizes winner selection and prize allocation. * Each winner is selected to be unique based on weighted random picks using a cumulative weights array and binary search. * @param _requestId The request ID of the VRF response. * @param _randomWords The array of random words provided by the VRF. */ function fulfillRandomWords( uint256 _requestId, uint256[] memory _randomWords ) internal override { require( s_requests[_requestId].paid > 0, "VerticalAIRaffle: Request not found" ); require( !s_requests[_requestId].fulfilled, "VerticalAIRaffle: Request already fulfilled" ); s_requests[_requestId].fulfilled = true; randomFulfilled = true; emit RequestFulfilled( _requestId, _randomWords, s_requests[_requestId].paid ); // If no participants exist, finalize without selecting winners. if (participants.length == 0) { prizesDistributed = true; lastRaffleEndTimestamp = block.timestamp; return; } // Build a cumulative weights array from participants based on their ticket weights. ( uint256[] memory cumulativeWeights, uint256 totalWeight ) = _getCumulativeWeights(); require(totalWeight > 0, "VerticalAIRaffle: Total weight is zero"); // Select winners based on weighted random picks using binary search. for (uint256 i = 0; i < prizeDistribution.length; i++) { uint256 rand = _randomWords[i] % totalWeight; uint256 target = rand + 1; uint256 index = _binarySearch(cumulativeWeights, target); address winner = participants[index]; // Ensure the winner is unique by using an offset uint256 offset = 0; while (selectedWinners[winner] && offset < participants.length) { offset++; uint256 newRand = (_randomWords[i] + offset) % totalWeight; target = newRand + 1; index = _binarySearch(cumulativeWeights, target); winner = participants[index]; } selectedWinners[winner] = true; winners.push(winner); } // Allocate prizes based on distribution percentages. for (uint256 i = 0; i < winners.length; i++) { uint256 prizeAmount = (prizePool * prizeDistribution[i]) / 100; winnerPrize[winners[i]] += prizeAmount; } prizesDistributed = true; lastRaffleEndTimestamp = block.timestamp; } /** * @notice Builds a cumulative weights array from participants based on their ticket weights. * @dev The weight used is the sum of tickets and bonus. * @return cumulativeWeights The cumulative weights array. * @return totalWeight The total weight sum. */ function _getCumulativeWeights() internal view returns (uint256[] memory cumulativeWeights, uint256 totalWeight) { cumulativeWeights = new uint256[](participants.length); uint256 sum = 0; for (uint256 i = 0; i < participants.length; i++) { uint256 weight = users[participants[i]].tickets + users[participants[i]].bonus; sum += weight; cumulativeWeights[i] = sum; } totalWeight = sum; } /** * @notice Performs a binary search on a sorted array to find the first index where the value is greater or equal to the target. * @param arr The sorted array of cumulative weights. * @param target The target value to search for. * @return The index in the array. */ function _binarySearch( uint256[] memory arr, uint256 target ) internal pure returns (uint256) { uint256 low = 0; uint256 high = arr.length - 1; while (low < high) { uint256 mid = low + (high - low) / 2; if (arr[mid] < target) { low = mid + 1; } else { high = mid; } } return low; } // ------------------------------------------------------- // Claiming Prizes (User) // ------------------------------------------------------- /** * @notice Allows a winner to claim their allocated USDC prize after the raffle ends and prizes are distributed. * @dev Checks that prizes are distributed, the caller hasn't claimed before, and then transfers the prize. */ function claimPrize() external nonReentrant { require( !raffleActive() && prizesDistributed, "VerticalAIRaffle: Prizes not distributed yet" ); uint256 amount = winnerPrize[msg.sender]; require( !users[msg.sender].hasClaimed, "VerticalAIRaffle: Already claimed" ); require(amount > 0, "VerticalAIRaffle: No prize for you"); // Add balance check for safety uint256 contractBalance = usdcToken.balanceOf(address(this)); require( contractBalance >= amount, "VerticalAIRaffle: Insufficient USDC balance" ); // Update state before transfer (checks-effects-interactions) users[msg.sender].hasClaimed = true; winnerPrize[msg.sender] = 0; // Transfer USDC directly to the winner usdcToken.safeTransfer(msg.sender, amount); emit PrizeClaimed(msg.sender, amount); } // ------------------------------------------------------- // Unstaking Tickets (User) // ------------------------------------------------------- /** * @notice Allows users to unstake all or a portion of their staked tokens. * @dev If no amount is provided (amount = 0), unstakes all tokens. * @param amount The amount of tokens to unstake, 0 for full unstake. */ function unstakeTickets(uint256 amount) external nonReentrant { User storage u = users[msg.sender]; require(u.tickets > 0, "VerticalAIRaffle: No tickets to unstake"); require(amount % ticketPrice == 0, "VerticalAIRaffle: Invalid amount"); // Calculate total staked tokens by summing all stakes. uint256 totalStaked = 0; for (uint256 i = 0; i < u.stakes.length; i++) { totalStaked += u.stakes[i].tokens; } // If amount is 0 or equals totalStaked, perform full unstake. if (amount == 0 || amount == totalStaked) { uint256 refundAmount = totalStaked; uint256 userTickets = u.tickets; uint256 userBonus = u.bonus; // Update global ticket count. totalTickets = totalTickets - userTickets - userBonus; // Reset user's ticket and bonus values. u.tickets = 0; u.bonus = 0; // Clear user's stakes array. while (u.stakes.length > 0) { u.stakes.pop(); } // Remove user from participants list. _removeParticipant(msg.sender); // Transfer all tokens back to the user. stakingToken.safeTransfer(msg.sender, refundAmount); emit TicketsUnstaked(msg.sender, userTickets, refundAmount); return; } // Partial unstake logic. require( amount <= totalStaked, "VerticalAIRaffle: Amount exceeds staked tokens" ); require(amount > 0, "VerticalAIRaffle: Invalid amount"); // Use a scaling factor (1e18) to avoid integer truncation. uint256 scale = 1e18; uint256 unstakePercentage = (amount * scale) / totalStaked; // Calculate tickets and bonus to remove with fixed-point math. uint256 ticketsToRemove = (u.tickets * unstakePercentage) / scale; uint256 bonusToRemove = (u.bonus * unstakePercentage) / scale; // Update global ticket count. totalTickets = totalTickets - ticketsToRemove - bonusToRemove; // Update user's tickets and bonus. u.tickets = u.tickets - ticketsToRemove; u.bonus = u.bonus - bonusToRemove; // Create a new stakes array with reduced amounts. Stakes[] memory newStakes = new Stakes[](u.stakes.length); uint256 newStakesCount = 0; for (uint256 i = 0; i < u.stakes.length; i++) { uint256 reducedAmount = u.stakes[i].tokens - ((u.stakes[i].tokens * unstakePercentage) / scale); if (reducedAmount > 0) { newStakes[newStakesCount] = Stakes({ tokens: reducedAmount, timeStaked: u.stakes[i].timeStaked }); newStakesCount++; } } // Clear existing stakes array. while (u.stakes.length > 0) { u.stakes.pop(); } // Add back the reduced stakes. for (uint256 i = 0; i < newStakesCount; i++) { u.stakes.push(newStakes[i]); } // Transfer the unstaked tokens back to the user. stakingToken.safeTransfer(msg.sender, amount); emit TicketsUnstaked(msg.sender, ticketsToRemove, amount); } /** * @dev Internal function to remove a participant from the participants array. * @param participant The address of the participant to remove. */ function _removeParticipant(address participant) internal { uint256 length = participants.length; for (uint256 i = 0; i < length; i++) { if (participants[i] == participant) { participants[i] = participants[length - 1]; participants.pop(); break; } } } // ------------------------------------------------------- // View Functions // ------------------------------------------------------- /** * @notice Calculates a user's weighted ticket count based on their stakes. * @dev Weight is calculated as sum((tokens * timeStaked) / 8640000) for all stakes. * @param user The address of the user. * @return weight The total weighted tickets of the user. */ function getUserWeight(address user) public view returns (uint256 weight) { User memory u = users[user]; uint256 currentTime = block.timestamp; for (uint256 i = 0; i < u.stakes.length; i++) { uint256 stakedTime = currentTime - u.stakes[i].timeStaked; weight += ((u.stakes[i].tokens * stakedTime) / 8640000); // Adjusted for decimals } return weight; } /** * @notice Retrieves the stakes of a specific user. * @param user The address of the user whose stakes are being queried. * @return An array of Stakes structures representing the user's stakes. */ function getUserStakes( address user ) external view returns (Stakes[] memory) { return users[user].stakes; } /** * @notice Retrieves the total number of tickets a user has, including bonus tickets. * @param user The address of the user whose tickets are being queried. * @return amount The total number of tickets the user has, including any bonus tickets. */ function getUserTickets(address user) public view returns (uint256 amount) { return (users[user].tickets + users[user].bonus); } /** * @notice Retrieves the total value of tokens held by a user based on their ticket count. * @param user The address of the user whose tokens are being queried. * @return The total value of tokens held by the user. */ function getUserTokens(address user) external view returns (uint256) { return users[user].tickets * ticketPrice; } /** * @notice Returns the total number of tickets sold in the current raffle. * @return The total count of tickets sold. */ function getTotalTickets() public view returns (uint256) { return totalTickets; } /** * @notice Returns the staking token balance of a given user. * @param user The address of the user whose staking token balance is to be retrieved. * @return The staking token balance of the user. */ function getStakingTokenBalance( address user ) external view returns (uint256) { return stakingToken.balanceOf(user); } /** * @notice Returns the total value of tokens from tickets sold in the current raffle. * @return The total value of tokens based on ticket sales. */ function getTotalTokens() external view returns (uint256) { uint256 totalTokens = 0; for (uint256 i = 0; i < participants.length; i++) { totalTokens += users[participants[i]].tickets * ticketPrice; } return totalTokens; } /** * @notice Returns the current prize distribution array. * @return The prize distribution array (in percentages). */ function getPrizeDistribution() external view returns (uint256[] memory) { return prizeDistribution; } /** * @notice Returns the current prize pool in USDC. * @return The total prize pool. */ function getPrizePool() external view returns (uint256) { return prizePool; } /** * @notice Calculates the total weighted sum of all participant tickets in the current raffle. * @return The total weighted sum of tickets. */ function getTotalWeight() public view returns (uint256) { uint256 totalWeighted; for (uint256 i = 0; i < participants.length; i++) { totalWeighted += getUserWeight(participants[i]); } return totalWeighted; } /** * @notice Returns the number of participants in the current raffle. * @return The total count of participants. */ function getNumberOfParticipants() external view returns (uint256) { return participants.length; } /** * @notice Checks if the raffle is currently active. * @return True if the current time is before the raffle end time, false otherwise. */ function raffleActive() public view returns (bool) { return block.timestamp < raffleEndTime; } /** * @notice Retrieves the list of winners selected after the raffle ended. * @return An array of addresses representing the winners. */ function getWinners() external view returns (address[] memory) { return winners; } /** * @notice Returns the prize amount allocated for a given winner address. * @param winner The address of the winner. * @return The prize amount in USDC. */ function getWinnerPrize(address winner) external view returns (uint256) { return winnerPrize[winner]; } // ------------------------------------------------------- // Admin Functions // ------------------------------------------------------- /** * @notice Allows the admin to withdraw unclaimed USDC funds after a certain delay post-raffle. * @dev Ensures users have time to claim their prizes and unstake before admin withdraws remaining funds. */ function withdrawUnclaimedFunds() external onlyOwner nonReentrant { require( !raffleActive() && prizesDistributed, "VerticalAIRaffle: Raffle not ended or not distributed" ); require( block.timestamp > lastRaffleEndTimestamp + UNCLAIMED_WITHDRAW_DELAY, "VerticalAIRaffle: Too early to withdraw" ); uint256 balance = usdcToken.balanceOf(address(this)); // Admin withdraws any remaining unclaimed USDC after the grace period. usdcToken.safeTransfer(owner(), balance); emit UnclaimedFundsWithdrawn(owner(), balance); } /** * @notice Allows the admin to withdraw any ERC20 token from the contract except USDC. * @param tokenAddress The address of the ERC20 token to withdraw. * @param amount The amount of the ERC20 token to withdraw. */ function emergencyWithdraw( address tokenAddress, uint256 amount ) external onlyOwner nonReentrant { require( tokenAddress != address(usdcToken), "VerticalAIRaffle: Cannot withdraw USDC" ); IERC20 token = IERC20(tokenAddress); uint256 balance = token.balanceOf(address(this)); require( amount <= balance, "VerticalAIRaffle: Insufficient token balance" ); token.safeTransfer(owner(), amount); emit EmergencyWithdraw(owner(), tokenAddress, amount); } /** * @notice Allows the admin to withdraw ETH from the contract. * @param amount The amount of ETH to withdraw. */ function emergencyWithdrawETH( uint256 amount ) external onlyOwner nonReentrant { uint256 balance = address(this).balance; require( amount <= balance, "VerticalAIRaffle: Insufficient ETH balance" ); (bool success, ) = owner().call{value: amount}(""); require(success, "VerticalAIRaffle: ETH transfer failed"); emit EmergencyWithdrawETH(owner(), amount); } /** * @notice Allows the admin to change the staking token. * @param _stakingToken The address of the new staking token. */ function setStakingToken(address _stakingToken) external onlyOwner { // Ensure the staking token address is not the zero address. require( _stakingToken != address(0), "VerticalAIRaffle: Invalid staking token" ); stakingToken = IERC20(_stakingToken); emit StakingTokenChanged(_stakingToken); } /** * @notice Allows the admin to change the VRF settings. * @param _callbackGasLimit The new callback gas limit. * @param _requestConfirmations The new number of request confirmations. */ function setVRFSettings( uint32 _callbackGasLimit, uint16 _requestConfirmations ) external onlyOwner { require( _callbackGasLimit > 0, "VerticalAIRaffle: Invalid callback gas limit" ); require( _requestConfirmations > 0, "VerticalAIRaffle: Invalid request confirmations" ); // Set the new callback gas limit. callbackGasLimit = _callbackGasLimit; // Set the new number of request confirmations. requestConfirmations = _requestConfirmations; // Reset random request flag to allow future requests. randomRequested = false; // Emit an event for the VRF settings change. emit VRFSettingsChanged(_callbackGasLimit, _requestConfirmations); } /** * @notice Calculates the sum of the prize distribution percentages. * @param _prizeDistribution The prize distribution percentages. * @return The sum of the prize distribution percentages. */ function _sumPrizeDistribution( uint256[] memory _prizeDistribution ) internal pure returns (uint256) { uint256 sum = 0; for (uint256 i = 0; i < _prizeDistribution.length; i++) { sum += _prizeDistribution[i]; } return sum; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at, * consider using {ReentrancyGuardTransient} instead. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard struct ReentrancyGuardStorage { uint256 _status; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) { assembly { $.slot := ReentrancyGuardStorageLocation } } /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); $._status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // On the first call to nonReentrant, _status will be NOT_ENTERED if ($._status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail $._status = ENTERED; } function _nonReentrantAfter() private { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) $._status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); return $._status == ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.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. * * The initial owner is set to the address provided by the deployer. 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 OwnableUpgradeable is Initializable, ContextUpgradeable { /// @custom:storage-location erc7201:openzeppelin.storage.Ownable struct OwnableStorage { address _owner; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; function _getOwnableStorage() private pure returns (OwnableStorage storage $) { assembly { $.slot := OwnableStorageLocation } } /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ function __Ownable_init(address initialOwner) internal onlyInitializing { __Ownable_init_unchained(initialOwner); } function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @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) { OwnableStorage storage $ = _getOwnableStorage(); return $._owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @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 { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { OwnableStorage storage $ = _getOwnableStorage(); address oldOwner = $._owner; $._owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC1363} from "../../../interfaces/IERC1363.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC-20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { /** * @dev An operation with an ERC-20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. * * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client" * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. * * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being * set here. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { safeTransfer(token, to, value); } else if (!token.transferAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * Reverts if the returned value is other than `true`. */ function transferFromAndCallRelaxed( IERC1363 token, address from, address to, uint256 value, bytes memory data ) internal { if (to.code.length == 0) { safeTransferFrom(token, from, to, value); } else if (!token.transferFromAndCall(from, to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when * targeting contracts. * * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}. * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall} * once without retrying, and relies on the returned value to be true. * * Reverts if the returned value is other than `true`. */ function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal { if (to.code.length == 0) { forceApprove(token, to, value); } else if (!token.approveAndCall(to, value, data)) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements. */ function _callOptionalReturn(IERC20 token, bytes memory data) private { uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) // bubble errors if iszero(success) { let ptr := mload(0x40) returndatacopy(ptr, 0, returndatasize()) revert(ptr, returndatasize()) } returnSize := returndatasize() returnValue := mload(0) } if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { bool success; uint256 returnSize; uint256 returnValue; assembly ("memory-safe") { success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20) returnSize := returndatasize() returnValue := mload(0) } return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {LinkTokenInterface} from "../../shared/interfaces/LinkTokenInterface.sol"; import {IVRFV2PlusWrapper} from "./interfaces/IVRFV2PlusWrapper.sol"; /** * * @notice Interface for contracts using VRF randomness through the VRF V2 wrapper * ******************************************************************************** * @dev PURPOSE * * @dev Create VRF V2+ requests without the need for subscription management. Rather than creating * @dev and funding a VRF V2+ subscription, a user can use this wrapper to create one off requests, * @dev paying up front rather than at fulfillment. * * @dev Since the price is determined using the gas price of the request transaction rather than * @dev the fulfillment transaction, the wrapper charges an additional premium on callback gas * @dev usage, in addition to some extra overhead costs associated with the VRFV2Wrapper contract. * ***************************************************************************** * @dev USAGE * * @dev Calling contracts must inherit from VRFV2PlusWrapperConsumerBase. The consumer must be funded * @dev with enough LINK or ether to make the request, otherwise requests will revert. To request randomness, * @dev call the 'requestRandomWords' function with the desired VRF parameters. This function handles * @dev paying for the request based on the current pricing. * * @dev Consumers must implement the fullfillRandomWords function, which will be called during * @dev fulfillment with the randomness result. */ abstract contract VRFV2PlusWrapperConsumerBase { error OnlyVRFWrapperCanFulfill(address have, address want); LinkTokenInterface internal immutable i_linkToken; IVRFV2PlusWrapper public immutable i_vrfV2PlusWrapper; /** * @param _vrfV2PlusWrapper is the address of the VRFV2Wrapper contract */ constructor(address _vrfV2PlusWrapper) { IVRFV2PlusWrapper vrfV2PlusWrapper = IVRFV2PlusWrapper(_vrfV2PlusWrapper); i_linkToken = LinkTokenInterface(vrfV2PlusWrapper.link()); i_vrfV2PlusWrapper = vrfV2PlusWrapper; } /** * @dev Requests randomness from the VRF V2+ wrapper. * * @param _callbackGasLimit is the gas limit that should be used when calling the consumer's * fulfillRandomWords function. * @param _requestConfirmations is the number of confirmations to wait before fulfilling the * request. A higher number of confirmations increases security by reducing the likelihood * that a chain re-org changes a published randomness outcome. * @param _numWords is the number of random words to request. * * @return requestId is the VRF V2+ request ID of the newly created randomness request. */ // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore function requestRandomness( uint32 _callbackGasLimit, uint16 _requestConfirmations, uint32 _numWords, bytes memory extraArgs ) internal returns (uint256 requestId, uint256 reqPrice) { reqPrice = i_vrfV2PlusWrapper.calculateRequestPrice(_callbackGasLimit, _numWords); i_linkToken.transferAndCall( address(i_vrfV2PlusWrapper), reqPrice, abi.encode(_callbackGasLimit, _requestConfirmations, _numWords, extraArgs) ); return (i_vrfV2PlusWrapper.lastRequestId(), reqPrice); } // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore function requestRandomnessPayInNative( uint32 _callbackGasLimit, uint16 _requestConfirmations, uint32 _numWords, bytes memory extraArgs ) internal returns (uint256 requestId, uint256 requestPrice) { requestPrice = i_vrfV2PlusWrapper.calculateRequestPriceNative(_callbackGasLimit, _numWords); return ( i_vrfV2PlusWrapper.requestRandomWordsInNative{value: requestPrice}( _callbackGasLimit, _requestConfirmations, _numWords, extraArgs ), requestPrice ); } /** * @notice fulfillRandomWords handles the VRF V2 wrapper response. The consuming contract must * @notice implement it. * * @param _requestId is the VRF V2 request ID. * @param _randomWords is the randomness result. */ // solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal virtual; function rawFulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) external { address vrfWrapperAddr = address(i_vrfV2PlusWrapper); if (msg.sender != vrfWrapperAddr) { revert OnlyVRFWrapperCanFulfill(msg.sender, vrfWrapperAddr); } fulfillRandomWords(_requestId, _randomWords); } /// @notice getBalance returns the native balance of the consumer contract function getBalance() public view returns (uint256) { return address(this).balance; } /// @notice getLinkToken returns the link token contract function getLinkToken() public view returns (LinkTokenInterface) { return i_linkToken; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; // End consumer library. library VRFV2PlusClient { // extraArgs will evolve to support new features bytes4 public constant EXTRA_ARGS_V1_TAG = bytes4(keccak256("VRF ExtraArgsV1")); struct ExtraArgsV1 { bool nativePayment; } struct RandomWordsRequest { bytes32 keyHash; uint256 subId; uint16 requestConfirmations; uint32 callbackGasLimit; uint32 numWords; bytes extraArgs; } function _argsToBytes(ExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) { return abi.encodeWithSelector(EXTRA_ARGS_V1_TAG, extraArgs); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @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 ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } 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 v5.1.0) (interfaces/IERC1363.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC165} from "./IERC165.sol"; /** * @title IERC1363 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363]. * * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction. */ interface IERC1363 is IERC20, IERC165 { /* * Note: the ERC-165 identifier for this interface is 0xb0202a11. * 0xb0202a11 === * bytes4(keccak256('transferAndCall(address,uint256)')) ^ * bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^ * bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^ * bytes4(keccak256('approveAndCall(address,uint256)')) ^ * bytes4(keccak256('approveAndCall(address,uint256,bytes)')) */ /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from the caller's account to `to` * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism * and then calls {IERC1363Receiver-onTransferReceived} on `to`. * @param from The address which you want to send tokens from. * @param to The address which you want to transfer to. * @param value The amount of tokens to be transferred. * @param data Additional data with no specified format, sent in call to `to`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value) external returns (bool); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`. * @param spender The address which will spend the funds. * @param value The amount of tokens to be spent. * @param data Additional data with no specified format, sent in call to `spender`. * @return A boolean value indicating whether the operation succeeded unless throwing. */ function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol) pragma solidity ^0.8.20; import {Errors} from "./Errors.sol"; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert Errors.InsufficientBalance(address(this).balance, amount); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert Errors.FailedCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {Errors.FailedCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert Errors.InsufficientBalance(address(this).balance, value); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case * of an unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {Errors.FailedCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly ("memory-safe") { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert Errors.FailedCall(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable-next-line interface-starts-with-i interface LinkTokenInterface { function allowance(address owner, address spender) external view returns (uint256 remaining); function approve(address spender, uint256 value) external returns (bool success); function balanceOf(address owner) external view returns (uint256 balance); function decimals() external view returns (uint8 decimalPlaces); function decreaseApproval(address spender, uint256 addedValue) external returns (bool success); function increaseApproval(address spender, uint256 subtractedValue) external; function name() external view returns (string memory tokenName); function symbol() external view returns (string memory tokenSymbol); function totalSupply() external view returns (uint256 totalTokensIssued); function transfer(address to, uint256 value) external returns (bool success); function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success); function transferFrom(address from, address to, uint256 value) external returns (bool success); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IVRFV2PlusWrapper { /** * @return the request ID of the most recent VRF V2 request made by this wrapper. This should only * be relied option within the same transaction that the request was made. */ function lastRequestId() external view returns (uint256); /** * @notice Calculates the price of a VRF request with the given callbackGasLimit at the current * @notice block. * * @dev This function relies on the transaction gas price which is not automatically set during * @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function. * * @param _callbackGasLimit is the gas limit used to estimate the price. * @param _numWords is the number of words to request. */ function calculateRequestPrice(uint32 _callbackGasLimit, uint32 _numWords) external view returns (uint256); /** * @notice Calculates the price of a VRF request in native with the given callbackGasLimit at the current * @notice block. * * @dev This function relies on the transaction gas price which is not automatically set during * @dev simulation. To estimate the price at a specific gas price, use the estimatePrice function. * * @param _callbackGasLimit is the gas limit used to estimate the price. * @param _numWords is the number of words to request. */ function calculateRequestPriceNative(uint32 _callbackGasLimit, uint32 _numWords) external view returns (uint256); /** * @notice Estimates the price of a VRF request with a specific gas limit and gas price. * * @dev This is a convenience function that can be called in simulation to better understand * @dev pricing. * * @param _callbackGasLimit is the gas limit used to estimate the price. * @param _numWords is the number of words to request. * @param _requestGasPriceWei is the gas price in wei used for the estimation. */ function estimateRequestPrice( uint32 _callbackGasLimit, uint32 _numWords, uint256 _requestGasPriceWei ) external view returns (uint256); /** * @notice Estimates the price of a VRF request in native with a specific gas limit and gas price. * * @dev This is a convenience function that can be called in simulation to better understand * @dev pricing. * * @param _callbackGasLimit is the gas limit used to estimate the price. * @param _numWords is the number of words to request. * @param _requestGasPriceWei is the gas price in wei used for the estimation. */ function estimateRequestPriceNative( uint32 _callbackGasLimit, uint32 _numWords, uint256 _requestGasPriceWei ) external view returns (uint256); /** * @notice Requests randomness from the VRF V2 wrapper, paying in native token. * * @param _callbackGasLimit is the gas limit for the request. * @param _requestConfirmations number of request confirmations to wait before serving a request. * @param _numWords is the number of words to request. */ function requestRandomWordsInNative( uint32 _callbackGasLimit, uint16 _requestConfirmations, uint32 _numWords, bytes calldata extraArgs ) external payable returns (uint256 requestId); function link() external view returns (address); function linkNativeFeed() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "../utils/introspection/IERC165.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol) pragma solidity ^0.8.20; /** * @dev Collection of common custom errors used in multiple contracts * * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library. * It is recommended to avoid relying on the error API for critical functionality. * * _Available since v5.1._ */ library Errors { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error InsufficientBalance(uint256 balance, uint256 needed); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedCall(); /** * @dev The deployment failed. */ error FailedDeployment(); /** * @dev A necessary precompile is missing. */ error MissingPrecompile(address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC-165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[ERC]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
{ "remappings": [ "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "@chainlink/=lib/chainlink/contracts/", "chainlink/=lib/chainlink/", "ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", "forge-std/=lib/forge-std/src/", "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"want","type":"address"}],"name":"OnlyVRFWrapperCanFulfill","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdrawETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","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":true,"internalType":"address","name":"winner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PrizeClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"prizePool","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"prizeDistribution","type":"uint256[]"}],"name":"RaffleConfigurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ticketPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"prizePool","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"prizeDistribution","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"raffleEndTime","type":"uint256"}],"name":"RaffleStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"randomWords","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"payment","type":"uint256"}],"name":"RequestFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"numWords","type":"uint32"}],"name":"RequestSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newStakingToken","type":"address"}],"name":"StakingTokenChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TicketsBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"refundAmount","type":"uint256"}],"name":"TicketsUnstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UnclaimedFundsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"callbackGasLimit","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"requestConfirmations","type":"uint16"}],"name":"VRFSettingsChanged","type":"event"},{"inputs":[],"name":"DISABLE_STAKING_BEFORE_END","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MULTIPLIER_ON_FRESH_RAFFLE","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RAFFLE_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNCLAIMED_WITHDRAW_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"buyTickets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"callbackGasLimit","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimPrize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enableNativePayment","type":"bool"}],"name":"distributePrizes","outputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"emergencyWithdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLinkToken","outputs":[{"internalType":"contract LinkTokenInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNumberOfParticipants","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPrizeDistribution","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPrizePool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getStakingTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalTickets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserStakes","outputs":[{"components":[{"internalType":"uint256","name":"tokens","type":"uint256"},{"internalType":"uint256","name":"timeStaked","type":"uint256"}],"internalType":"struct VerticalAIRaffle.Stakes[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserTickets","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserWeight","outputs":[{"internalType":"uint256","name":"weight","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"winner","type":"address"}],"name":"getWinnerPrize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWinners","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"i_vrfV2PlusWrapper","outputs":[{"internalType":"contract IVRFV2PlusWrapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"address","name":"_usdcToken","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastRaffleEndTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRequestId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numWords","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"participants","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"prizeDistribution","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prizePool","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"prizesDistributed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"raffleActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"raffleEndTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"raffleStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randomFulfilled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"randomRequested","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_requestId","type":"uint256"},{"internalType":"uint256[]","name":"_randomWords","type":"uint256[]"}],"name":"rawFulfillRandomWords","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestConfirmations","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"requestIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"s_requests","outputs":[{"internalType":"uint256","name":"paid","type":"uint256"},{"internalType":"bool","name":"fulfilled","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"selectedWinners","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_prizePool","type":"uint256"},{"internalType":"uint256[]","name":"_prizeDistribution","type":"uint256[]"}],"name":"setRaffleConfiguration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_stakingToken","type":"address"}],"name":"setStakingToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_callbackGasLimit","type":"uint32"},{"internalType":"uint16","name":"_requestConfirmations","type":"uint16"}],"name":"setVRFSettings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingDisabledTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_ticketPrice","type":"uint256"},{"internalType":"uint256","name":"_prizePool","type":"uint256"},{"internalType":"uint256[]","name":"_prizeDistribution","type":"uint256[]"}],"name":"startNewRaffle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ticketPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalTickets","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":"uint256","name":"amount","type":"uint256"}],"name":"unstakeTickets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdcToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"users","outputs":[{"internalType":"uint256","name":"tickets","type":"uint256"},{"internalType":"uint256","name":"bonus","type":"uint256"},{"internalType":"bool","name":"hasClaimed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"winnerPrize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"winners","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawUnclaimedFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60c060405234801561000f575f5ffd5b507302aae1a04f9828517b3007f83f6181900cad910c5f819050806001600160a01b0316631c4695f46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610065573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061008991906100a1565b6001600160a01b039081166080521660a052506100ce565b5f602082840312156100b1575f5ffd5b81516001600160a01b03811681146100c7575f5ffd5b9392505050565b60805160a0516143976101205f395f818161080a01528181610f28015281816136f10152818161375e0152818161382a015281816138c6015261396d01525f8181610ab0015261389701526143975ff3fe60806040526004361061039d575f3560e01c80638796ba8c116101de578063c80c28a211610108578063df15c37e1161009d578063f08b82e61161006d578063f08b82e614610af3578063f2fde38b14610b07578063f95d937f14610b26578063fc2a88c314610b39575f5ffd5b8063df15c37e14610a6b578063e544a55414610a8c578063e76d516814610aa2578063eb6f780114610ad4575f5ffd5b8063cbd3f962116100d8578063cbd3f96214610a0b578063d30d155514610a2c578063d6a6cf0114610a40578063dd11247e14610a56575f5ffd5b8063c80c28a2146109a4578063c925c680146109b8578063c9905469146109d7578063caba2d87146109ed575f5ffd5b80639ed0868d1161017e578063a87430ba1161014e578063a87430ba146108c9578063a9d148f914610926578063b0fb162f14610951578063c0c53b8b14610985575f5ffd5b80639ed0868d146107f9578063a168fa891461082c578063a2fb117514610876578063a4ba364d14610895575f5ffd5b80638da5cb5b116101b95780638da5cb5b1461078257806394f289011461079657806395ccea67146107ac578063977639eb146107cb575f5ffd5b80638796ba8c14610736578063884bf67c146107555780638d7fe47814610769575f5ffd5b806335c1d349116102ca5780636b792c4b1161025f57806372f702f31161022f57806372f702f3146106aa5780637ccfd7fc146106c85780638071e9ab146106eb578063842e29811461070a575f5ffd5b80636b792c4b1461064e57806370740ac91461066d578063715018a614610681578063719ce73e14610695575f5ffd5b8063519dc8d21161029a578063519dc8d2146105dd57806354c237b7146105fc5780635e7c981f1461061b5780636a4d0ce71461062f575f5ffd5b806335c1d349146105805780633be539ee1461059f57806340490a90146105b457806349d0343d146105c8575f5ffd5b806316ec39001161034057806324f746971161031057806324f74697146104e25780632777724b146105195780632f3666371461054257806332acf41014610561575f5ffd5b806316ec39001461046e57806318a32e701461048f5780631e9b12ef146104a45780631fe543e3146104c3575f5ffd5b80630a9e766d1161037b5780630a9e766d146103fb57806311eac8551461041057806312065fe0146104475780631209b1f614610459575f5ffd5b80630484a22f146103a157806306aba0e1146103d357806306e8337f146103e7575b5f5ffd5b3480156103ac575f5ffd5b506103c06103bb366004613cee565b610b4e565b6040519081526020015b60405180910390f35b3480156103de575f5ffd5b506103c0610b80565b3480156103f2575f5ffd5b506006546103c0565b348015610406575f5ffd5b506103c060105481565b34801561041b575f5ffd5b5060015461042f906001600160a01b031681565b6040516001600160a01b0390911681526020016103ca565b348015610452575f5ffd5b50476103c0565b348015610464575f5ffd5b506103c060055481565b348015610479575f5ffd5b5061048d610488366004613dbe565b610bd7565b005b34801561049a575f5ffd5b506103c060035481565b3480156104af575f5ffd5b5061048d6104be366004613cee565b610eb1565b3480156104ce575f5ffd5b5061048d6104dd366004613e0a565b610f26565b3480156104ed575f5ffd5b506013546105049062010000900463ffffffff1681565b60405163ffffffff90911681526020016103ca565b348015610524575f5ffd5b506009546105329060ff1681565b60405190151581526020016103ca565b34801561054d575f5ffd5b5061048d61055c366004613e4e565b610f90565b34801561056c575f5ffd5b506103c061057b366004613cee565b61136e565b34801561058b575f5ffd5b5061042f61059a366004613e4e565b6113da565b3480156105aa575f5ffd5b506103c060025481565b3480156105bf575f5ffd5b506103c0611402565b3480156105d3575f5ffd5b506103c060045481565b3480156105e8575f5ffd5b506103c06105f7366004613cee565b61147a565b348015610607575f5ffd5b506103c0610616366004613e4e565b6114a3565b348015610626575f5ffd5b50610504600381565b34801561063a575f5ffd5b5061048d610649366004613e0a565b6114c2565b348015610659575f5ffd5b5061048d610668366004613e4e565b6115c4565b348015610678575f5ffd5b5061048d61174e565b34801561068c575f5ffd5b5061048d611a01565b3480156106a0575f5ffd5b506103c060075481565b3480156106b5575f5ffd5b505f5461042f906001600160a01b031681565b3480156106d3575f5ffd5b5060135461050490600160401b900463ffffffff1681565b3480156106f6575f5ffd5b5061048d610705366004613e65565b611a12565b348015610715575f5ffd5b50610729610724366004613cee565b611b6a565b6040516103ca9190613eaa565b348015610741575f5ffd5b506103c0610750366004613e4e565b611bef565b348015610760575f5ffd5b506007546103c0565b348015610774575f5ffd5b506013546105329060ff1681565b34801561078d575f5ffd5b5061042f611bfe565b3480156107a1575f5ffd5b506103c062093a8081565b3480156107b7575f5ffd5b5061048d6107c6366004613ef8565b611c2c565b3480156107d6575f5ffd5b506105326107e5366004613cee565b600f6020525f908152604090205460ff1681565b348015610804575f5ffd5b5061042f7f000000000000000000000000000000000000000000000000000000000000000081565b348015610837575f5ffd5b50610861610846366004613e4e565b600e6020525f90815260409020805460019091015460ff1682565b604080519283529015156020830152016103ca565b348015610881575f5ffd5b5061042f610890366004613e4e565b611e06565b3480156108a0575f5ffd5b506103c06108af366004613cee565b6001600160a01b03165f908152600d602052604090205490565b3480156108d4575f5ffd5b506109096108e3366004613cee565b600a6020525f908152604090206001810154600282015460039092015490919060ff1683565b6040805193845260208401929092521515908201526060016103ca565b348015610931575f5ffd5b506103c0610940366004613cee565b600d6020525f908152604090205481565b34801561095c575f5ffd5b5060135461097290600160301b900461ffff1681565b60405161ffff90911681526020016103ca565b348015610990575f5ffd5b5061048d61099f366004613f20565b611e15565b3480156109af575f5ffd5b50600b546103c0565b3480156109c3575f5ffd5b506103c06109d2366004613cee565b612056565b3480156109e2575f5ffd5b506003544210610532565b3480156109f8575f5ffd5b5060135461053290610100900460ff1681565b348015610a16575f5ffd5b50610a1f612198565b6040516103ca9190613f9a565b348015610a37575f5ffd5b5061048d6121ee565b348015610a4b575f5ffd5b506103c06203f48081565b348015610a61575f5ffd5b506103c060065481565b348015610a76575f5ffd5b50610a7f6123d6565b6040516103ca9190613fac565b348015610a97575f5ffd5b506103c0621baf8081565b348015610aad575f5ffd5b507f000000000000000000000000000000000000000000000000000000000000000061042f565b348015610adf575f5ffd5b5061048d610aee366004613e4e565b612435565b348015610afe575f5ffd5b506103c0612976565b348015610b12575f5ffd5b5061048d610b21366004613cee565b6129e2565b6103c0610b34366004613ff9565b612a1c565b348015610b44575f5ffd5b506103c060125481565b6001600160a01b0381165f908152600a602052604081206002810154600190910154610b7a9190614028565b92915050565b5f80805b600b54811015610bd157610bbd600b8281548110610ba457610ba461403b565b5f918252602090912001546001600160a01b0316612056565b610bc79083614028565b9150600101610b84565b50919050565b610bdf612d2c565b600354421015610c4a5760405162461bcd60e51b815260206004820152602b60248201527f566572746963616c4149526166666c653a2050726576696f757320726166666c60448201526a19481b9bdd08195b99195960aa1b60648201526084015b60405180910390fd5b62093a80601054610c5b9190614028565b4211610cc05760405162461bcd60e51b815260206004820152602e60248201527f566572746963616c4149526166666c653a20556e636c61696d65642066756e6460448201526d1cc81cdd1a5b1b081b1bd8dad95960921b6064820152608401610c41565b670de0b6b3a7640000831015610d4f5760405162461bcd60e51b815260206004820152604860248201527f566572746963616c4149526166666c653a20496e76616c6964207469636b657460448201527f207072696365202d206d757374206265206174206c656173742031205665727460648201526720a4903a37b5b2b760c11b608482015260a401610c41565b620f4240821015610dbf5760405162461bcd60e51b815260206004820152603460248201527f566572746963616c4149526166666c653a205072697a6520706f6f6c206d757360448201527374206265206174206c656173742031205553444360601b6064820152608401610c41565b5f815111610ddf5760405162461bcd60e51b8152600401610c419061404f565b610de881612d5e565b606414610e075760405162461bcd60e51b8152600401610c419061409b565b610e0f612da1565b600583905560078290558051610e2c906008906020840190613c68565b50426002819055610e4190621baf8090614028565b6003819055610e54906203f480906140f8565b6004556009805460ff191690556013805461ffff191690556003546040517f35da57820653539490fe7fabfe0d2e329e0da82d8c51f2d7c11d46f448136d7e91610ea4918691869186919061410b565b60405180910390a1505050565b610eb9612d2c565b6001600160a01b038116610edf5760405162461bcd60e51b8152600401610c419061413a565b5f80546001600160a01b0319166001600160a01b038316908117825560405190917fb6f764d747255946dedc96c174b74d4e49b325b5bc52409477464edf6424279191a250565b7f0000000000000000000000000000000000000000000000000000000000000000336001600160a01b03821614610f81576040516345d498b760e11b81523360048201526001600160a01b0382166024820152604401610c41565b610f8b8383612fbf565b505050565b610f986133e4565b6003544210610ff55760405162461bcd60e51b8152602060048201526024808201527f566572746963616c4149526166666c653a205374616b696e67206e6f742061636044820152637469766560e01b6064820152608401610c41565b60045442106110515760405162461bcd60e51b815260206004820152602260248201527f566572746963616c4149526166666c653a205374616b696e672064697361626c604482015261195960f21b6064820152608401610c41565b5f81116110ac5760405162461bcd60e51b8152602060048201526024808201527f566572746963616c4149526166666c653a20416d6f756e74206d7573742062656044820152630203e20360e41b6064820152608401610c41565b6005546110b99082614195565b156110d65760405162461bcd60e51b8152600401610c41906141a8565b5f600554826110e591906141dd565b5f8054604051636eb1769f60e11b815233600482015230602482015292935090916001600160a01b039091169063dd62ed3e90604401602060405180830381865afa158015611136573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061115a91906141f0565b9050828110156111f85760405162461bcd60e51b815260206004820152605b60248201527f566572746963616c4149526166666c653a20496e73756666696369656e74206160448201527f6c6c6f77616e63652e20506c6561736520617070726f76652074686520636f6e60648201527f747261637420746f207370656e6420796f757220746f6b656e732e0000000000608482015260a401610c41565b5f5461120f906001600160a01b031633308661341b565b5f611218611402565b335f908152600a60205260408120600181015492935091900361127757600b80546001810182555f919091527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180546001600160a01b031916331790555b835f6112846001856140f8565b61128e9087614207565b905081836001015f8282546112a39190614028565b9250508190555080836002015f8282546112bd9190614028565b9091555050604080518082019091528781524260208083019182528554600181810188555f888152929092209351600290910290930192835590519101556113058183614028565b60065f8282546113159190614028565b909155505060405186815233907fb24ee662b0a257d0321d324e737d2507fbe45df8af8d0da951f630f3754eb6a09060200160405180910390a250505050505061136b60015f5160206143425f395f51905f5255565b50565b5f80546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a0823190602401602060405180830381865afa1580156113b6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b7a91906141f0565b600b81815481106113e9575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f61140e600354421090565b61141757505f90565b5f600354421161144157621baf8060035461143291906140f8565b61143c90426140f8565b611446565b621baf805b905062093a8081101561145b57600391505090565b6212750081101561146e57600291505090565b600191505090565b5090565b6005546001600160a01b0382165f908152600a60205260408120600101549091610b7a91614207565b600881815481106114b2575f80fd5b5f91825260209091200154905081565b6114ca612d2c565b60035442106115265760405162461bcd60e51b815260206004820152602260248201527f566572746963616c4149526166666c653a204e6f2061637469766520726166666044820152616c6560f01b6064820152608401610c41565b5f8151116115465760405162461bcd60e51b8152600401610c419061404f565b61154f81612d5e565b60641461156e5760405162461bcd60e51b8152600401610c419061409b565b60078290558051611586906008906020840190613c68565b507fb38a37a8a375aed3f2e61de6faeb96a617a6589888b0560855e466a02fd02ba482826040516115b892919061421e565b60405180910390a15050565b6115cc612d2c565b6115d46133e4565b47808211156116385760405162461bcd60e51b815260206004820152602a60248201527f566572746963616c4149526166666c653a20496e73756666696369656e74204560448201526954482062616c616e636560b01b6064820152608401610c41565b5f611641611bfe565b6001600160a01b0316836040515f6040518083038185875af1925050503d805f8114611688576040519150601f19603f3d011682016040523d82523d5f602084013e61168d565b606091505b50509050806116ec5760405162461bcd60e51b815260206004820152602560248201527f566572746963616c4149526166666c653a20455448207472616e736665722066604482015264185a5b195960da1b6064820152608401610c41565b6116f4611bfe565b6001600160a01b03167f20f907b58305c7b76035bc03b26f32b1c4f6560f96be6f3bb54c5c848a2d4ddd8460405161172e91815260200190565b60405180910390a2505061136b60015f5160206143425f395f51905f5255565b6117566133e4565b600354421015801561176a575060095460ff165b6117cb5760405162461bcd60e51b815260206004820152602c60248201527f566572746963616c4149526166666c653a205072697a6573206e6f742064697360448201526b1d1c9a589d5d1959081e595d60a21b6064820152608401610c41565b335f908152600d6020908152604080832054600a9092529091206003015460ff16156118435760405162461bcd60e51b815260206004820152602160248201527f566572746963616c4149526166666c653a20416c726561647920636c61696d656044820152601960fa1b6064820152608401610c41565b5f811161189d5760405162461bcd60e51b815260206004820152602260248201527f566572746963616c4149526166666c653a204e6f207072697a6520666f7220796044820152616f7560f01b6064820152608401610c41565b6001546040516370a0823160e01b81523060048201525f916001600160a01b0316906370a0823190602401602060405180830381865afa1580156118e3573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061190791906141f0565b90508181101561196d5760405162461bcd60e51b815260206004820152602b60248201527f566572746963616c4149526166666c653a20496e73756666696369656e74205560448201526a5344432062616c616e636560a81b6064820152608401610c41565b335f818152600a60209081526040808320600301805460ff19166001908117909155600d909252822091909155546119b2916001600160a01b0391909116908461349b565b60405182815233907f95681e512bc0fe659e195e06c283eada494316f3d801213e48e7101af92bf7709060200160405180910390a250506119ff60015f5160206143425f395f51905f5255565b565b611a09612d2c565b6119ff5f6134cc565b611a1a612d2c565b5f8263ffffffff1611611a845760405162461bcd60e51b815260206004820152602c60248201527f566572746963616c4149526166666c653a20496e76616c69642063616c6c626160448201526b18dac819d85cc81b1a5b5a5d60a21b6064820152608401610c41565b5f8161ffff1611611aef5760405162461bcd60e51b815260206004820152602f60248201527f566572746963616c4149526166666c653a20496e76616c69642072657175657360448201526e7420636f6e6669726d6174696f6e7360881b6064820152608401610c41565b6013805467ffffffffffff000019166201000063ffffffff851690810267ffff000000000000191691909117600160301b61ffff85169081029190911760ff19169092556040805191825260208201929092527f2c77336ab81188a8742449f8d547af50c4d0b7f7f870ffaf4684100f7817d7d791016115b8565b6001600160a01b0381165f908152600a60209081526040808320805482518185028101850190935280835260609492939192909184015b82821015611be4578382905f5260205f2090600202016040518060400160405290815f820154815260200160018201548152505081526020019060010190611ba1565b505050509050919050565b601181815481106114b2575f80fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b611c34612d2c565b611c3c6133e4565b6001546001600160a01b0390811690831603611ca95760405162461bcd60e51b815260206004820152602660248201527f566572746963616c4149526166666c653a2043616e6e6f74207769746864726160448201526577205553444360d01b6064820152608401610c41565b6040516370a0823160e01b815230600482015282905f906001600160a01b038316906370a0823190602401602060405180830381865afa158015611cef573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d1391906141f0565b905080831115611d7a5760405162461bcd60e51b815260206004820152602c60248201527f566572746963616c4149526166666c653a20496e73756666696369656e74207460448201526b6f6b656e2062616c616e636560a01b6064820152608401610c41565b611d96611d85611bfe565b6001600160a01b038416908561349b565b836001600160a01b0316611da8611bfe565b6001600160a01b03167ff24ef89f38eadc1bde50701ad6e4d6d11a2dc24f7cf834a486991f388332850485604051611de291815260200190565b60405180910390a35050611e0260015f5160206143425f395f51905f5255565b5050565b600c81815481106113e9575f80fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f81158015611e5a5750825b90505f8267ffffffffffffffff166001148015611e765750303b155b905081158015611e84575080155b15611ea25760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611ecc57845460ff60401b1916600160401b1785555b6001600160a01b038816611f225760405162461bcd60e51b815260206004820152601f60248201527f566572746963616c4149526166666c653a20496e76616c6964206f776e6572006044820152606401610c41565b6001600160a01b038716611f485760405162461bcd60e51b8152600401610c419061413a565b6001600160a01b038616611faa5760405162461bcd60e51b8152602060048201526024808201527f566572746963616c4149526166666c653a20496e76616c69642055534443207460448201526337b5b2b760e11b6064820152608401610c41565b611fb38861353c565b611fbb61354d565b5f80546001600160a01b03808a166001600160a01b0319928316179092556001805492891692909116919091179055601380546603000c3500000067ffffffffffff000019909116179055831561204c57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b6001600160a01b0381165f908152600a602090815260408083208151815460a09481028201850190935260808101838152859491938492849190879085015b828210156120d8578382905f5260205f2090600202016040518060400160405290815f820154815260200160018201548152505081526020019060010190612095565b50505090825250600182015460208201526002820154604082015260039091015460ff1615156060909101529050425f5b825151811015612190575f835f015182815181106121295761212961403b565b6020026020010151602001518361214091906140f8565b90506283d60081855f0151848151811061215c5761215c61403b565b60200260200101515f01516121719190614207565b61217b91906141dd565b6121859086614028565b945050600101612109565b505050919050565b606060088054806020026020016040519081016040528092919081815260200182805480156121e457602002820191905f5260205f20905b8154815260200190600101908083116121d0575b5050505050905090565b6121f6612d2c565b6121fe6133e4565b6003544210158015612212575060095460ff165b61227c5760405162461bcd60e51b815260206004820152603560248201527f566572746963616c4149526166666c653a20526166666c65206e6f7420656e646044820152741959081bdc881b9bdd08191a5cdd1c9a589d5d1959605a1b6064820152608401610c41565b62093a8060105461228d9190614028565b42116122eb5760405162461bcd60e51b815260206004820152602760248201527f566572746963616c4149526166666c653a20546f6f206561726c7920746f20776044820152666974686472617760c81b6064820152608401610c41565b6001546040516370a0823160e01b81523060048201525f916001600160a01b0316906370a0823190602401602060405180830381865afa158015612331573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061235591906141f0565b9050612375612362611bfe565b6001546001600160a01b0316908361349b565b61237d611bfe565b6001600160a01b03167f231018073b4766f5e529d23208cd2857e988073cdbddca91cdc1191a3f37762c826040516123b791815260200190565b60405180910390a2506119ff60015f5160206143425f395f51905f5255565b6060600c8054806020026020016040519081016040528092919081815260200182805480156121e457602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161240e575050505050905090565b61243d6133e4565b335f908152600a6020526040902060018101546124ac5760405162461bcd60e51b815260206004820152602760248201527f566572746963616c4149526166666c653a204e6f207469636b65747320746f20604482015266756e7374616b6560c81b6064820152608401610c41565b6005546124b99083614195565b156124d65760405162461bcd60e51b8152600401610c41906141a8565b5f805b825481101561251a57825f0181815481106124f6576124f661403b565b905f5260205f2090600202015f0154826125109190614028565b91506001016124d9565b5082158061252757508083145b15612602576001820154600283015460065483929190819061254a9084906140f8565b61255491906140f8565b6006555f6001860181905560028601555b84541561259e57845485908061257d5761257d61423e565b5f8281526020812060025f1990930192830201818155600101559055612565565b6125a73361355d565b5f546125bd906001600160a01b0316338561349b565b604080518381526020810185905233917f1298075b78824c91d07d5091a4da323884397a08b94f9293acf6b4e9bdf76c98910160405180910390a25050505050612960565b808311156126695760405162461bcd60e51b815260206004820152602e60248201527f566572746963616c4149526166666c653a20416d6f756e74206578636565647360448201526d207374616b656420746f6b656e7360901b6064820152608401610c41565b5f83116126885760405162461bcd60e51b8152600401610c41906141a8565b670de0b6b3a76400005f8261269d8387614207565b6126a791906141dd565b90505f828286600101546126bb9190614207565b6126c591906141dd565b90505f838387600201546126d99190614207565b6126e391906141dd565b905080826006546126f491906140f8565b6126fe91906140f8565b60065560018601546127119083906140f8565b600187015560028601546127269082906140f8565b600287015585545f9067ffffffffffffffff81111561274757612747613d0e565b60405190808252806020026020018201604052801561278b57816020015b604080518082019091525f80825260208201528152602001906001900390816127655790505b5090505f805b8854811015612876575f87878b5f0184815481106127b1576127b161403b565b905f5260205f2090600202015f01546127ca9190614207565b6127d491906141dd565b8a5f0183815481106127e8576127e861403b565b905f5260205f2090600202015f015461280191906140f8565b9050801561286d5760405180604001604052808281526020018b5f01848154811061282e5761282e61403b565b905f5260205f209060020201600101548152508484815181106128535761285361403b565b6020026020010181905250828061286990614252565b9350505b50600101612791565b505b8754156128b15787548890806128905761289061423e565b5f8281526020812060025f1990930192830201818155600101559055612878565b5f5b8181101561290557885f018382815181106128d0576128d061403b565b6020908102919091018101518254600181810185555f94855293839020825160029092020190815591015190820155016128b3565b505f5461291c906001600160a01b0316338b61349b565b60408051858152602081018b905233917f1298075b78824c91d07d5091a4da323884397a08b94f9293acf6b4e9bdf76c98910160405180910390a250505050505050505b61136b60015f5160206143425f395f51905f5255565b5f80805b600b54811015610bd157600554600a5f600b848154811061299d5761299d61403b565b5f9182526020808320909101546001600160a01b031683528201929092526040019020600101546129ce9190614207565b6129d89083614028565b915060010161297a565b6129ea612d2c565b6001600160a01b038116612a1357604051631e4fbdf760e01b81525f6004820152602401610c41565b61136b816134cc565b5f612a25612d2c565b612a2d6133e4565b600354421015612a8d5760405162461bcd60e51b815260206004820152602560248201527f566572746963616c4149526166666c653a20526166666c65207374696c6c2061604482015264637469766560d81b6064820152608401610c41565b60095460ff1615612af55760405162461bcd60e51b815260206004820152602c60248201527f566572746963616c4149526166666c653a205072697a657320616c726561647960448201526b08191a5cdd1c9a589d5d195960a21b6064820152608401610c41565b60135460ff1615612b5f5760405162461bcd60e51b815260206004820152602e60248201527f566572746963616c4149526166666c653a2052616e646f6d6e65737320616c7260448201526d1958591e481c995c5d595cdd195960921b6064820152608401610c41565b6008546013805463ffffffff909216600160401b026bffffffff000000000000000019909216919091179055604080516020810190915282151581525f90612ba690613651565b90505f8315612be957601354612bdf9063ffffffff62010000820481169161ffff600160301b82041691600160401b90910416856136c2565b9093509050612c1f565b601354612c199063ffffffff62010000820481169161ffff600160301b82041691600160401b90910416856137fb565b90935090505b604080516060810182528281525f602080830182815284518381528083018652848601908152888452600e83529490922083518155915160018301805460ff19169115159190911790559251805192939192612c819260028501920190613c68565b505060118054600180820183555f929092527f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c680185905560128590556013805460ff191690911790819055604051600160401b90910463ffffffff1681528491507fcc58b13ad3eab50626c6a6300b1d139cd6ebb1688a7cced9461c2f7e762665ee9060200160405180910390a25050612d2760015f5160206143425f395f51905f5255565b919050565b33612d35611bfe565b6001600160a01b0316146119ff5760405163118cdaa760e01b8152336004820152602401610c41565b5f80805b8351811015612d9a57838181518110612d7d57612d7d61403b565b602002602001015182612d909190614028565b9150600101612d62565b5092915050565b5f600b805480602002602001604051908101604052809291908181526020018280548015612df657602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612dd8575b50505f60068190559394505050505b600c54811015612e5857600f5f600c8381548110612e2557612e2561403b565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19169055600101612e05565b50612e64600b5f613cad565b612e6f600c5f613cad565b5f5b8151811015611e02575f828281518110612e8d57612e8d61403b565b6020908102919091018101516001600160a01b0381165f908152600a909252604090912060018101549192509015612f62576001810154612ecf600382614207565b612ed991906140f8565b6002820190815560038201805460ff19169055600b8054600181810183555f929092527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180546001600160a01b0319166001600160a01b038616179055905490820154612f479190614028565b60065f828254612f579190614028565b90915550612fb59050565b5f60018201819055600282015560038101805460ff191690555b805415612fb5578054819080612f9457612f9461423e565b5f8281526020812060025f1990930192830201818155600101559055612f7c565b5050600101612e71565b5f828152600e60205260409020546130255760405162461bcd60e51b815260206004820152602360248201527f566572746963616c4149526166666c653a2052657175657374206e6f7420666f6044820152621d5b9960ea1b6064820152608401610c41565b5f828152600e602052604090206001015460ff161561309a5760405162461bcd60e51b815260206004820152602b60248201527f566572746963616c4149526166666c653a205265717565737420616c7265616460448201526a1e48199d5b199a5b1b195960aa1b6064820152608401610c41565b5f828152600e6020526040908190206001818101805460ff191690911790556013805461ff00191661010017905554905183917f147eb1ff0c82f87f2b03e2c43f5a36488ff63ec6b730195fde4605f612f8db51916130fa91859161426a565b60405180910390a2600b545f036131205750506009805460ff1916600117905542601055565b5f5f61312a6139eb565b915091505f811161318c5760405162461bcd60e51b815260206004820152602660248201527f566572746963616c4149526166666c653a20546f74616c20776569676874206960448201526573207a65726f60d01b6064820152608401610c41565b5f5b60085481101561332f575f828583815181106131ac576131ac61403b565b60200260200101516131be9190614195565b90505f6131cc826001614028565b90505f6131d98683613b1c565b90505f600b82815481106131ef576131ef61403b565b5f9182526020822001546001600160a01b031691505b6001600160a01b0382165f908152600f602052604090205460ff16801561322d5750600b5481105b156132bb578061323c81614252565b9150505f87828b89815181106132545761325461403b565b60200260200101516132669190614028565b6132709190614195565b905061327d816001614028565b94506132898986613b1c565b9350600b848154811061329e5761329e61403b565b5f918252602090912001546001600160a01b031692506132059050565b506001600160a01b03165f818152600f60205260408120805460ff19166001908117909155600c8054808301825592527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c790910180546001600160a01b03191690921790915593909301925061318e915050565b505f5b600c548110156133cc575f6064600883815481106133525761335261403b565b905f5260205f2001546007546133689190614207565b61337291906141dd565b905080600d5f600c858154811061338b5761338b61403b565b5f9182526020808320909101546001600160a01b03168352820192909252604001812080549091906133be908490614028565b909155505050600101613332565b50506009805460ff1916600117905550504260105550565b5f5160206143425f395f51905f5280546001190161341557604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b6040516001600160a01b0384811660248301528381166044830152606482018390526134829186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613ba3565b50505050565b60015f5160206143425f395f51905f5255565b6040516001600160a01b03838116602483015260448201839052610f8b91859182169063a9059cbb90606401613450565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b613544613c0f565b61136b81613c58565b613555613c0f565b6119ff613c60565b600b545f5b81811015610f8b57826001600160a01b0316600b82815481106135875761358761403b565b5f918252602090912001546001600160a01b03160361364957600b6135ad6001846140f8565b815481106135bd576135bd61403b565b5f91825260209091200154600b80546001600160a01b0390921691839081106135e8576135e861403b565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550600b8054806136245761362461423e565b5f8281526020902081015f1990810180546001600160a01b0319169055019055505050565b600101613562565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa8260405160240161368a91511515815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915292915050565b6040516313c34b7f60e01b815263ffffffff8086166004830152831660248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906313c34b7f90604401602060405180830381865afa158015613736573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061375a91906141f0565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639cfc058e82888888886040518663ffffffff1660e01b81526004016137af94939291906142b9565b60206040518083038185885af11580156137cb573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906137f091906141f0565b915094509492505050565b6040516313f2e28560e11b815263ffffffff8086166004830152831660248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906327e5c50a90604401602060405180830381865afa15801561386f573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061389391906141f0565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634000aea07f000000000000000000000000000000000000000000000000000000000000000083898989896040516020016138fd94939291906142b9565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161392a939291906142f7565b6020604051808303815f875af1158015613946573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061396a9190614326565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fc2a88c36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156139c7573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137f091906141f0565b600b546060905f9067ffffffffffffffff811115613a0b57613a0b613d0e565b604051908082528060200260200182016040528015613a34578160200160208202803683370190505b5091505f805b600b54811015613b15575f600a5f600b8481548110613a5b57613a5b61403b565b905f5260205f20015f9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020019081526020015f2060020154600a5f600b8581548110613aaf57613aaf61403b565b5f9182526020808320909101546001600160a01b03168352820192909252604001902060010154613ae09190614028565b9050613aec8184614028565b925082858381518110613b0157613b0161403b565b602090810291909101015250600101613a3a565b5091929050565b5f5f5f90505f60018551613b3091906140f8565b90505b80821015613b9b575f6002613b4884846140f8565b613b5291906141dd565b613b5c9084614028565b905084868281518110613b7157613b7161403b565b60200260200101511015613b9157613b8a816001614028565b9250613b95565b8091505b50613b33565b509392505050565b5f5f60205f8451602086015f885af180613bc2576040513d5f823e3d81fd5b50505f513d91508115613bd9578060011415613be6565b6001600160a01b0384163b155b1561348257604051635274afe760e01b81526001600160a01b0385166004820152602401610c41565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166119ff57604051631afcd79f60e31b815260040160405180910390fd5b6129ea613c0f565b613488613c0f565b828054828255905f5260205f20908101928215613ca1579160200282015b82811115613ca1578251825591602001919060010190613c86565b50611476929150613cc4565b5080545f8255905f5260205f209081019061136b91905b5b80821115611476575f8155600101613cc5565b80356001600160a01b0381168114612d27575f5ffd5b5f60208284031215613cfe575f5ffd5b613d0782613cd8565b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112613d31575f5ffd5b813567ffffffffffffffff811115613d4b57613d4b613d0e565b8060051b604051601f19603f830116810181811067ffffffffffffffff82111715613d7857613d78613d0e565b604052918252602081850181019290810186841115613d95575f5ffd5b6020860192505b83831015613db4578235815260209283019201613d9c565b5095945050505050565b5f5f5f60608486031215613dd0575f5ffd5b8335925060208401359150604084013567ffffffffffffffff811115613df4575f5ffd5b613e0086828701613d22565b9150509250925092565b5f5f60408385031215613e1b575f5ffd5b82359150602083013567ffffffffffffffff811115613e38575f5ffd5b613e4485828601613d22565b9150509250929050565b5f60208284031215613e5e575f5ffd5b5035919050565b5f5f60408385031215613e76575f5ffd5b823563ffffffff81168114613e89575f5ffd5b9150602083013561ffff81168114613e9f575f5ffd5b809150509250929050565b602080825282518282018190525f918401906040840190835b81811015613eed578351805184526020908101518185015290930192604090920191600101613ec3565b509095945050505050565b5f5f60408385031215613f09575f5ffd5b613f1283613cd8565b946020939093013593505050565b5f5f5f60608486031215613f32575f5ffd5b613f3b84613cd8565b9250613f4960208501613cd8565b9150613f5760408501613cd8565b90509250925092565b5f8151808452602084019350602083015f5b82811015613f90578151865260209586019590910190600101613f72565b5093949350505050565b602081525f613d076020830184613f60565b602080825282518282018190525f918401906040840190835b81811015613eed5783516001600160a01b0316835260209384019390920191600101613fc5565b801515811461136b575f5ffd5b5f60208284031215614009575f5ffd5b8135613d0781613fec565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610b7a57610b7a614014565b634e487b7160e01b5f52603260045260245ffd5b6020808252602c908201527f566572746963616c4149526166666c653a20496e76616c6964207072697a652060408201526b3234b9ba3934b13aba34b7b760a11b606082015260800190565b60208082526039908201527f566572746963616c4149526166666c653a205072697a6520646973747269627560408201527f74696f6e206d7573742062652065786163746c79203130302500000000000000606082015260800190565b81810381811115610b7a57610b7a614014565b848152836020820152608060408201525f6141296080830185613f60565b905082606083015295945050505050565b60208082526027908201527f566572746963616c4149526166666c653a20496e76616c6964207374616b696e60408201526633903a37b5b2b760c91b606082015260800190565b634e487b7160e01b5f52601260045260245ffd5b5f826141a3576141a3614181565b500690565b6020808252818101527f566572746963616c4149526166666c653a20496e76616c696420616d6f756e74604082015260600190565b5f826141eb576141eb614181565b500490565b5f60208284031215614200575f5ffd5b5051919050565b8082028115828204841417610b7a57610b7a614014565b828152604060208201525f6142366040830184613f60565b949350505050565b634e487b7160e01b5f52603160045260245ffd5b5f6001820161426357614263614014565b5060010190565b604081525f61427c6040830185613f60565b90508260208301529392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b63ffffffff8516815261ffff8416602082015263ffffffff83166040820152608060608201525f6142ed608083018461428b565b9695505050505050565b60018060a01b0384168152826020820152606060408201525f61431d606083018461428b565b95945050505050565b5f60208284031215614336575f5ffd5b8151613d0781613fec56fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220089ec3dd1e3798df05d73ee94b014d7a446bab82d14dbf22c67e7f028514640964736f6c634300081c0033
Deployed Bytecode
0x60806040526004361061039d575f3560e01c80638796ba8c116101de578063c80c28a211610108578063df15c37e1161009d578063f08b82e61161006d578063f08b82e614610af3578063f2fde38b14610b07578063f95d937f14610b26578063fc2a88c314610b39575f5ffd5b8063df15c37e14610a6b578063e544a55414610a8c578063e76d516814610aa2578063eb6f780114610ad4575f5ffd5b8063cbd3f962116100d8578063cbd3f96214610a0b578063d30d155514610a2c578063d6a6cf0114610a40578063dd11247e14610a56575f5ffd5b8063c80c28a2146109a4578063c925c680146109b8578063c9905469146109d7578063caba2d87146109ed575f5ffd5b80639ed0868d1161017e578063a87430ba1161014e578063a87430ba146108c9578063a9d148f914610926578063b0fb162f14610951578063c0c53b8b14610985575f5ffd5b80639ed0868d146107f9578063a168fa891461082c578063a2fb117514610876578063a4ba364d14610895575f5ffd5b80638da5cb5b116101b95780638da5cb5b1461078257806394f289011461079657806395ccea67146107ac578063977639eb146107cb575f5ffd5b80638796ba8c14610736578063884bf67c146107555780638d7fe47814610769575f5ffd5b806335c1d349116102ca5780636b792c4b1161025f57806372f702f31161022f57806372f702f3146106aa5780637ccfd7fc146106c85780638071e9ab146106eb578063842e29811461070a575f5ffd5b80636b792c4b1461064e57806370740ac91461066d578063715018a614610681578063719ce73e14610695575f5ffd5b8063519dc8d21161029a578063519dc8d2146105dd57806354c237b7146105fc5780635e7c981f1461061b5780636a4d0ce71461062f575f5ffd5b806335c1d349146105805780633be539ee1461059f57806340490a90146105b457806349d0343d146105c8575f5ffd5b806316ec39001161034057806324f746971161031057806324f74697146104e25780632777724b146105195780632f3666371461054257806332acf41014610561575f5ffd5b806316ec39001461046e57806318a32e701461048f5780631e9b12ef146104a45780631fe543e3146104c3575f5ffd5b80630a9e766d1161037b5780630a9e766d146103fb57806311eac8551461041057806312065fe0146104475780631209b1f614610459575f5ffd5b80630484a22f146103a157806306aba0e1146103d357806306e8337f146103e7575b5f5ffd5b3480156103ac575f5ffd5b506103c06103bb366004613cee565b610b4e565b6040519081526020015b60405180910390f35b3480156103de575f5ffd5b506103c0610b80565b3480156103f2575f5ffd5b506006546103c0565b348015610406575f5ffd5b506103c060105481565b34801561041b575f5ffd5b5060015461042f906001600160a01b031681565b6040516001600160a01b0390911681526020016103ca565b348015610452575f5ffd5b50476103c0565b348015610464575f5ffd5b506103c060055481565b348015610479575f5ffd5b5061048d610488366004613dbe565b610bd7565b005b34801561049a575f5ffd5b506103c060035481565b3480156104af575f5ffd5b5061048d6104be366004613cee565b610eb1565b3480156104ce575f5ffd5b5061048d6104dd366004613e0a565b610f26565b3480156104ed575f5ffd5b506013546105049062010000900463ffffffff1681565b60405163ffffffff90911681526020016103ca565b348015610524575f5ffd5b506009546105329060ff1681565b60405190151581526020016103ca565b34801561054d575f5ffd5b5061048d61055c366004613e4e565b610f90565b34801561056c575f5ffd5b506103c061057b366004613cee565b61136e565b34801561058b575f5ffd5b5061042f61059a366004613e4e565b6113da565b3480156105aa575f5ffd5b506103c060025481565b3480156105bf575f5ffd5b506103c0611402565b3480156105d3575f5ffd5b506103c060045481565b3480156105e8575f5ffd5b506103c06105f7366004613cee565b61147a565b348015610607575f5ffd5b506103c0610616366004613e4e565b6114a3565b348015610626575f5ffd5b50610504600381565b34801561063a575f5ffd5b5061048d610649366004613e0a565b6114c2565b348015610659575f5ffd5b5061048d610668366004613e4e565b6115c4565b348015610678575f5ffd5b5061048d61174e565b34801561068c575f5ffd5b5061048d611a01565b3480156106a0575f5ffd5b506103c060075481565b3480156106b5575f5ffd5b505f5461042f906001600160a01b031681565b3480156106d3575f5ffd5b5060135461050490600160401b900463ffffffff1681565b3480156106f6575f5ffd5b5061048d610705366004613e65565b611a12565b348015610715575f5ffd5b50610729610724366004613cee565b611b6a565b6040516103ca9190613eaa565b348015610741575f5ffd5b506103c0610750366004613e4e565b611bef565b348015610760575f5ffd5b506007546103c0565b348015610774575f5ffd5b506013546105329060ff1681565b34801561078d575f5ffd5b5061042f611bfe565b3480156107a1575f5ffd5b506103c062093a8081565b3480156107b7575f5ffd5b5061048d6107c6366004613ef8565b611c2c565b3480156107d6575f5ffd5b506105326107e5366004613cee565b600f6020525f908152604090205460ff1681565b348015610804575f5ffd5b5061042f7f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c81565b348015610837575f5ffd5b50610861610846366004613e4e565b600e6020525f90815260409020805460019091015460ff1682565b604080519283529015156020830152016103ca565b348015610881575f5ffd5b5061042f610890366004613e4e565b611e06565b3480156108a0575f5ffd5b506103c06108af366004613cee565b6001600160a01b03165f908152600d602052604090205490565b3480156108d4575f5ffd5b506109096108e3366004613cee565b600a6020525f908152604090206001810154600282015460039092015490919060ff1683565b6040805193845260208401929092521515908201526060016103ca565b348015610931575f5ffd5b506103c0610940366004613cee565b600d6020525f908152604090205481565b34801561095c575f5ffd5b5060135461097290600160301b900461ffff1681565b60405161ffff90911681526020016103ca565b348015610990575f5ffd5b5061048d61099f366004613f20565b611e15565b3480156109af575f5ffd5b50600b546103c0565b3480156109c3575f5ffd5b506103c06109d2366004613cee565b612056565b3480156109e2575f5ffd5b506003544210610532565b3480156109f8575f5ffd5b5060135461053290610100900460ff1681565b348015610a16575f5ffd5b50610a1f612198565b6040516103ca9190613f9a565b348015610a37575f5ffd5b5061048d6121ee565b348015610a4b575f5ffd5b506103c06203f48081565b348015610a61575f5ffd5b506103c060065481565b348015610a76575f5ffd5b50610a7f6123d6565b6040516103ca9190613fac565b348015610a97575f5ffd5b506103c0621baf8081565b348015610aad575f5ffd5b507f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca61042f565b348015610adf575f5ffd5b5061048d610aee366004613e4e565b612435565b348015610afe575f5ffd5b506103c0612976565b348015610b12575f5ffd5b5061048d610b21366004613cee565b6129e2565b6103c0610b34366004613ff9565b612a1c565b348015610b44575f5ffd5b506103c060125481565b6001600160a01b0381165f908152600a602052604081206002810154600190910154610b7a9190614028565b92915050565b5f80805b600b54811015610bd157610bbd600b8281548110610ba457610ba461403b565b5f918252602090912001546001600160a01b0316612056565b610bc79083614028565b9150600101610b84565b50919050565b610bdf612d2c565b600354421015610c4a5760405162461bcd60e51b815260206004820152602b60248201527f566572746963616c4149526166666c653a2050726576696f757320726166666c60448201526a19481b9bdd08195b99195960aa1b60648201526084015b60405180910390fd5b62093a80601054610c5b9190614028565b4211610cc05760405162461bcd60e51b815260206004820152602e60248201527f566572746963616c4149526166666c653a20556e636c61696d65642066756e6460448201526d1cc81cdd1a5b1b081b1bd8dad95960921b6064820152608401610c41565b670de0b6b3a7640000831015610d4f5760405162461bcd60e51b815260206004820152604860248201527f566572746963616c4149526166666c653a20496e76616c6964207469636b657460448201527f207072696365202d206d757374206265206174206c656173742031205665727460648201526720a4903a37b5b2b760c11b608482015260a401610c41565b620f4240821015610dbf5760405162461bcd60e51b815260206004820152603460248201527f566572746963616c4149526166666c653a205072697a6520706f6f6c206d757360448201527374206265206174206c656173742031205553444360601b6064820152608401610c41565b5f815111610ddf5760405162461bcd60e51b8152600401610c419061404f565b610de881612d5e565b606414610e075760405162461bcd60e51b8152600401610c419061409b565b610e0f612da1565b600583905560078290558051610e2c906008906020840190613c68565b50426002819055610e4190621baf8090614028565b6003819055610e54906203f480906140f8565b6004556009805460ff191690556013805461ffff191690556003546040517f35da57820653539490fe7fabfe0d2e329e0da82d8c51f2d7c11d46f448136d7e91610ea4918691869186919061410b565b60405180910390a1505050565b610eb9612d2c565b6001600160a01b038116610edf5760405162461bcd60e51b8152600401610c419061413a565b5f80546001600160a01b0319166001600160a01b038316908117825560405190917fb6f764d747255946dedc96c174b74d4e49b325b5bc52409477464edf6424279191a250565b7f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c336001600160a01b03821614610f81576040516345d498b760e11b81523360048201526001600160a01b0382166024820152604401610c41565b610f8b8383612fbf565b505050565b610f986133e4565b6003544210610ff55760405162461bcd60e51b8152602060048201526024808201527f566572746963616c4149526166666c653a205374616b696e67206e6f742061636044820152637469766560e01b6064820152608401610c41565b60045442106110515760405162461bcd60e51b815260206004820152602260248201527f566572746963616c4149526166666c653a205374616b696e672064697361626c604482015261195960f21b6064820152608401610c41565b5f81116110ac5760405162461bcd60e51b8152602060048201526024808201527f566572746963616c4149526166666c653a20416d6f756e74206d7573742062656044820152630203e20360e41b6064820152608401610c41565b6005546110b99082614195565b156110d65760405162461bcd60e51b8152600401610c41906141a8565b5f600554826110e591906141dd565b5f8054604051636eb1769f60e11b815233600482015230602482015292935090916001600160a01b039091169063dd62ed3e90604401602060405180830381865afa158015611136573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061115a91906141f0565b9050828110156111f85760405162461bcd60e51b815260206004820152605b60248201527f566572746963616c4149526166666c653a20496e73756666696369656e74206160448201527f6c6c6f77616e63652e20506c6561736520617070726f76652074686520636f6e60648201527f747261637420746f207370656e6420796f757220746f6b656e732e0000000000608482015260a401610c41565b5f5461120f906001600160a01b031633308661341b565b5f611218611402565b335f908152600a60205260408120600181015492935091900361127757600b80546001810182555f919091527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180546001600160a01b031916331790555b835f6112846001856140f8565b61128e9087614207565b905081836001015f8282546112a39190614028565b9250508190555080836002015f8282546112bd9190614028565b9091555050604080518082019091528781524260208083019182528554600181810188555f888152929092209351600290910290930192835590519101556113058183614028565b60065f8282546113159190614028565b909155505060405186815233907fb24ee662b0a257d0321d324e737d2507fbe45df8af8d0da951f630f3754eb6a09060200160405180910390a250505050505061136b60015f5160206143425f395f51905f5255565b50565b5f80546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a0823190602401602060405180830381865afa1580156113b6573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b7a91906141f0565b600b81815481106113e9575f80fd5b5f918252602090912001546001600160a01b0316905081565b5f61140e600354421090565b61141757505f90565b5f600354421161144157621baf8060035461143291906140f8565b61143c90426140f8565b611446565b621baf805b905062093a8081101561145b57600391505090565b6212750081101561146e57600291505090565b600191505090565b5090565b6005546001600160a01b0382165f908152600a60205260408120600101549091610b7a91614207565b600881815481106114b2575f80fd5b5f91825260209091200154905081565b6114ca612d2c565b60035442106115265760405162461bcd60e51b815260206004820152602260248201527f566572746963616c4149526166666c653a204e6f2061637469766520726166666044820152616c6560f01b6064820152608401610c41565b5f8151116115465760405162461bcd60e51b8152600401610c419061404f565b61154f81612d5e565b60641461156e5760405162461bcd60e51b8152600401610c419061409b565b60078290558051611586906008906020840190613c68565b507fb38a37a8a375aed3f2e61de6faeb96a617a6589888b0560855e466a02fd02ba482826040516115b892919061421e565b60405180910390a15050565b6115cc612d2c565b6115d46133e4565b47808211156116385760405162461bcd60e51b815260206004820152602a60248201527f566572746963616c4149526166666c653a20496e73756666696369656e74204560448201526954482062616c616e636560b01b6064820152608401610c41565b5f611641611bfe565b6001600160a01b0316836040515f6040518083038185875af1925050503d805f8114611688576040519150601f19603f3d011682016040523d82523d5f602084013e61168d565b606091505b50509050806116ec5760405162461bcd60e51b815260206004820152602560248201527f566572746963616c4149526166666c653a20455448207472616e736665722066604482015264185a5b195960da1b6064820152608401610c41565b6116f4611bfe565b6001600160a01b03167f20f907b58305c7b76035bc03b26f32b1c4f6560f96be6f3bb54c5c848a2d4ddd8460405161172e91815260200190565b60405180910390a2505061136b60015f5160206143425f395f51905f5255565b6117566133e4565b600354421015801561176a575060095460ff165b6117cb5760405162461bcd60e51b815260206004820152602c60248201527f566572746963616c4149526166666c653a205072697a6573206e6f742064697360448201526b1d1c9a589d5d1959081e595d60a21b6064820152608401610c41565b335f908152600d6020908152604080832054600a9092529091206003015460ff16156118435760405162461bcd60e51b815260206004820152602160248201527f566572746963616c4149526166666c653a20416c726561647920636c61696d656044820152601960fa1b6064820152608401610c41565b5f811161189d5760405162461bcd60e51b815260206004820152602260248201527f566572746963616c4149526166666c653a204e6f207072697a6520666f7220796044820152616f7560f01b6064820152608401610c41565b6001546040516370a0823160e01b81523060048201525f916001600160a01b0316906370a0823190602401602060405180830381865afa1580156118e3573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061190791906141f0565b90508181101561196d5760405162461bcd60e51b815260206004820152602b60248201527f566572746963616c4149526166666c653a20496e73756666696369656e74205560448201526a5344432062616c616e636560a81b6064820152608401610c41565b335f818152600a60209081526040808320600301805460ff19166001908117909155600d909252822091909155546119b2916001600160a01b0391909116908461349b565b60405182815233907f95681e512bc0fe659e195e06c283eada494316f3d801213e48e7101af92bf7709060200160405180910390a250506119ff60015f5160206143425f395f51905f5255565b565b611a09612d2c565b6119ff5f6134cc565b611a1a612d2c565b5f8263ffffffff1611611a845760405162461bcd60e51b815260206004820152602c60248201527f566572746963616c4149526166666c653a20496e76616c69642063616c6c626160448201526b18dac819d85cc81b1a5b5a5d60a21b6064820152608401610c41565b5f8161ffff1611611aef5760405162461bcd60e51b815260206004820152602f60248201527f566572746963616c4149526166666c653a20496e76616c69642072657175657360448201526e7420636f6e6669726d6174696f6e7360881b6064820152608401610c41565b6013805467ffffffffffff000019166201000063ffffffff851690810267ffff000000000000191691909117600160301b61ffff85169081029190911760ff19169092556040805191825260208201929092527f2c77336ab81188a8742449f8d547af50c4d0b7f7f870ffaf4684100f7817d7d791016115b8565b6001600160a01b0381165f908152600a60209081526040808320805482518185028101850190935280835260609492939192909184015b82821015611be4578382905f5260205f2090600202016040518060400160405290815f820154815260200160018201548152505081526020019060010190611ba1565b505050509050919050565b601181815481106114b2575f80fd5b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b611c34612d2c565b611c3c6133e4565b6001546001600160a01b0390811690831603611ca95760405162461bcd60e51b815260206004820152602660248201527f566572746963616c4149526166666c653a2043616e6e6f74207769746864726160448201526577205553444360d01b6064820152608401610c41565b6040516370a0823160e01b815230600482015282905f906001600160a01b038316906370a0823190602401602060405180830381865afa158015611cef573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d1391906141f0565b905080831115611d7a5760405162461bcd60e51b815260206004820152602c60248201527f566572746963616c4149526166666c653a20496e73756666696369656e74207460448201526b6f6b656e2062616c616e636560a01b6064820152608401610c41565b611d96611d85611bfe565b6001600160a01b038416908561349b565b836001600160a01b0316611da8611bfe565b6001600160a01b03167ff24ef89f38eadc1bde50701ad6e4d6d11a2dc24f7cf834a486991f388332850485604051611de291815260200190565b60405180910390a35050611e0260015f5160206143425f395f51905f5255565b5050565b600c81815481106113e9575f80fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f81158015611e5a5750825b90505f8267ffffffffffffffff166001148015611e765750303b155b905081158015611e84575080155b15611ea25760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315611ecc57845460ff60401b1916600160401b1785555b6001600160a01b038816611f225760405162461bcd60e51b815260206004820152601f60248201527f566572746963616c4149526166666c653a20496e76616c6964206f776e6572006044820152606401610c41565b6001600160a01b038716611f485760405162461bcd60e51b8152600401610c419061413a565b6001600160a01b038616611faa5760405162461bcd60e51b8152602060048201526024808201527f566572746963616c4149526166666c653a20496e76616c69642055534443207460448201526337b5b2b760e11b6064820152608401610c41565b611fb38861353c565b611fbb61354d565b5f80546001600160a01b03808a166001600160a01b0319928316179092556001805492891692909116919091179055601380546603000c3500000067ffffffffffff000019909116179055831561204c57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b6001600160a01b0381165f908152600a602090815260408083208151815460a09481028201850190935260808101838152859491938492849190879085015b828210156120d8578382905f5260205f2090600202016040518060400160405290815f820154815260200160018201548152505081526020019060010190612095565b50505090825250600182015460208201526002820154604082015260039091015460ff1615156060909101529050425f5b825151811015612190575f835f015182815181106121295761212961403b565b6020026020010151602001518361214091906140f8565b90506283d60081855f0151848151811061215c5761215c61403b565b60200260200101515f01516121719190614207565b61217b91906141dd565b6121859086614028565b945050600101612109565b505050919050565b606060088054806020026020016040519081016040528092919081815260200182805480156121e457602002820191905f5260205f20905b8154815260200190600101908083116121d0575b5050505050905090565b6121f6612d2c565b6121fe6133e4565b6003544210158015612212575060095460ff165b61227c5760405162461bcd60e51b815260206004820152603560248201527f566572746963616c4149526166666c653a20526166666c65206e6f7420656e646044820152741959081bdc881b9bdd08191a5cdd1c9a589d5d1959605a1b6064820152608401610c41565b62093a8060105461228d9190614028565b42116122eb5760405162461bcd60e51b815260206004820152602760248201527f566572746963616c4149526166666c653a20546f6f206561726c7920746f20776044820152666974686472617760c81b6064820152608401610c41565b6001546040516370a0823160e01b81523060048201525f916001600160a01b0316906370a0823190602401602060405180830381865afa158015612331573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061235591906141f0565b9050612375612362611bfe565b6001546001600160a01b0316908361349b565b61237d611bfe565b6001600160a01b03167f231018073b4766f5e529d23208cd2857e988073cdbddca91cdc1191a3f37762c826040516123b791815260200190565b60405180910390a2506119ff60015f5160206143425f395f51905f5255565b6060600c8054806020026020016040519081016040528092919081815260200182805480156121e457602002820191905f5260205f20905b81546001600160a01b0316815260019091019060200180831161240e575050505050905090565b61243d6133e4565b335f908152600a6020526040902060018101546124ac5760405162461bcd60e51b815260206004820152602760248201527f566572746963616c4149526166666c653a204e6f207469636b65747320746f20604482015266756e7374616b6560c81b6064820152608401610c41565b6005546124b99083614195565b156124d65760405162461bcd60e51b8152600401610c41906141a8565b5f805b825481101561251a57825f0181815481106124f6576124f661403b565b905f5260205f2090600202015f0154826125109190614028565b91506001016124d9565b5082158061252757508083145b15612602576001820154600283015460065483929190819061254a9084906140f8565b61255491906140f8565b6006555f6001860181905560028601555b84541561259e57845485908061257d5761257d61423e565b5f8281526020812060025f1990930192830201818155600101559055612565565b6125a73361355d565b5f546125bd906001600160a01b0316338561349b565b604080518381526020810185905233917f1298075b78824c91d07d5091a4da323884397a08b94f9293acf6b4e9bdf76c98910160405180910390a25050505050612960565b808311156126695760405162461bcd60e51b815260206004820152602e60248201527f566572746963616c4149526166666c653a20416d6f756e74206578636565647360448201526d207374616b656420746f6b656e7360901b6064820152608401610c41565b5f83116126885760405162461bcd60e51b8152600401610c41906141a8565b670de0b6b3a76400005f8261269d8387614207565b6126a791906141dd565b90505f828286600101546126bb9190614207565b6126c591906141dd565b90505f838387600201546126d99190614207565b6126e391906141dd565b905080826006546126f491906140f8565b6126fe91906140f8565b60065560018601546127119083906140f8565b600187015560028601546127269082906140f8565b600287015585545f9067ffffffffffffffff81111561274757612747613d0e565b60405190808252806020026020018201604052801561278b57816020015b604080518082019091525f80825260208201528152602001906001900390816127655790505b5090505f805b8854811015612876575f87878b5f0184815481106127b1576127b161403b565b905f5260205f2090600202015f01546127ca9190614207565b6127d491906141dd565b8a5f0183815481106127e8576127e861403b565b905f5260205f2090600202015f015461280191906140f8565b9050801561286d5760405180604001604052808281526020018b5f01848154811061282e5761282e61403b565b905f5260205f209060020201600101548152508484815181106128535761285361403b565b6020026020010181905250828061286990614252565b9350505b50600101612791565b505b8754156128b15787548890806128905761289061423e565b5f8281526020812060025f1990930192830201818155600101559055612878565b5f5b8181101561290557885f018382815181106128d0576128d061403b565b6020908102919091018101518254600181810185555f94855293839020825160029092020190815591015190820155016128b3565b505f5461291c906001600160a01b0316338b61349b565b60408051858152602081018b905233917f1298075b78824c91d07d5091a4da323884397a08b94f9293acf6b4e9bdf76c98910160405180910390a250505050505050505b61136b60015f5160206143425f395f51905f5255565b5f80805b600b54811015610bd157600554600a5f600b848154811061299d5761299d61403b565b5f9182526020808320909101546001600160a01b031683528201929092526040019020600101546129ce9190614207565b6129d89083614028565b915060010161297a565b6129ea612d2c565b6001600160a01b038116612a1357604051631e4fbdf760e01b81525f6004820152602401610c41565b61136b816134cc565b5f612a25612d2c565b612a2d6133e4565b600354421015612a8d5760405162461bcd60e51b815260206004820152602560248201527f566572746963616c4149526166666c653a20526166666c65207374696c6c2061604482015264637469766560d81b6064820152608401610c41565b60095460ff1615612af55760405162461bcd60e51b815260206004820152602c60248201527f566572746963616c4149526166666c653a205072697a657320616c726561647960448201526b08191a5cdd1c9a589d5d195960a21b6064820152608401610c41565b60135460ff1615612b5f5760405162461bcd60e51b815260206004820152602e60248201527f566572746963616c4149526166666c653a2052616e646f6d6e65737320616c7260448201526d1958591e481c995c5d595cdd195960921b6064820152608401610c41565b6008546013805463ffffffff909216600160401b026bffffffff000000000000000019909216919091179055604080516020810190915282151581525f90612ba690613651565b90505f8315612be957601354612bdf9063ffffffff62010000820481169161ffff600160301b82041691600160401b90910416856136c2565b9093509050612c1f565b601354612c199063ffffffff62010000820481169161ffff600160301b82041691600160401b90910416856137fb565b90935090505b604080516060810182528281525f602080830182815284518381528083018652848601908152888452600e83529490922083518155915160018301805460ff19169115159190911790559251805192939192612c819260028501920190613c68565b505060118054600180820183555f929092527f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c680185905560128590556013805460ff191690911790819055604051600160401b90910463ffffffff1681528491507fcc58b13ad3eab50626c6a6300b1d139cd6ebb1688a7cced9461c2f7e762665ee9060200160405180910390a25050612d2760015f5160206143425f395f51905f5255565b919050565b33612d35611bfe565b6001600160a01b0316146119ff5760405163118cdaa760e01b8152336004820152602401610c41565b5f80805b8351811015612d9a57838181518110612d7d57612d7d61403b565b602002602001015182612d909190614028565b9150600101612d62565b5092915050565b5f600b805480602002602001604051908101604052809291908181526020018280548015612df657602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311612dd8575b50505f60068190559394505050505b600c54811015612e5857600f5f600c8381548110612e2557612e2561403b565b5f9182526020808320909101546001600160a01b031683528201929092526040019020805460ff19169055600101612e05565b50612e64600b5f613cad565b612e6f600c5f613cad565b5f5b8151811015611e02575f828281518110612e8d57612e8d61403b565b6020908102919091018101516001600160a01b0381165f908152600a909252604090912060018101549192509015612f62576001810154612ecf600382614207565b612ed991906140f8565b6002820190815560038201805460ff19169055600b8054600181810183555f929092527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90180546001600160a01b0319166001600160a01b038616179055905490820154612f479190614028565b60065f828254612f579190614028565b90915550612fb59050565b5f60018201819055600282015560038101805460ff191690555b805415612fb5578054819080612f9457612f9461423e565b5f8281526020812060025f1990930192830201818155600101559055612f7c565b5050600101612e71565b5f828152600e60205260409020546130255760405162461bcd60e51b815260206004820152602360248201527f566572746963616c4149526166666c653a2052657175657374206e6f7420666f6044820152621d5b9960ea1b6064820152608401610c41565b5f828152600e602052604090206001015460ff161561309a5760405162461bcd60e51b815260206004820152602b60248201527f566572746963616c4149526166666c653a205265717565737420616c7265616460448201526a1e48199d5b199a5b1b195960aa1b6064820152608401610c41565b5f828152600e6020526040908190206001818101805460ff191690911790556013805461ff00191661010017905554905183917f147eb1ff0c82f87f2b03e2c43f5a36488ff63ec6b730195fde4605f612f8db51916130fa91859161426a565b60405180910390a2600b545f036131205750506009805460ff1916600117905542601055565b5f5f61312a6139eb565b915091505f811161318c5760405162461bcd60e51b815260206004820152602660248201527f566572746963616c4149526166666c653a20546f74616c20776569676874206960448201526573207a65726f60d01b6064820152608401610c41565b5f5b60085481101561332f575f828583815181106131ac576131ac61403b565b60200260200101516131be9190614195565b90505f6131cc826001614028565b90505f6131d98683613b1c565b90505f600b82815481106131ef576131ef61403b565b5f9182526020822001546001600160a01b031691505b6001600160a01b0382165f908152600f602052604090205460ff16801561322d5750600b5481105b156132bb578061323c81614252565b9150505f87828b89815181106132545761325461403b565b60200260200101516132669190614028565b6132709190614195565b905061327d816001614028565b94506132898986613b1c565b9350600b848154811061329e5761329e61403b565b5f918252602090912001546001600160a01b031692506132059050565b506001600160a01b03165f818152600f60205260408120805460ff19166001908117909155600c8054808301825592527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c790910180546001600160a01b03191690921790915593909301925061318e915050565b505f5b600c548110156133cc575f6064600883815481106133525761335261403b565b905f5260205f2001546007546133689190614207565b61337291906141dd565b905080600d5f600c858154811061338b5761338b61403b565b5f9182526020808320909101546001600160a01b03168352820192909252604001812080549091906133be908490614028565b909155505050600101613332565b50506009805460ff1916600117905550504260105550565b5f5160206143425f395f51905f5280546001190161341557604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b6040516001600160a01b0384811660248301528381166044830152606482018390526134829186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613ba3565b50505050565b60015f5160206143425f395f51905f5255565b6040516001600160a01b03838116602483015260448201839052610f8b91859182169063a9059cbb90606401613450565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b613544613c0f565b61136b81613c58565b613555613c0f565b6119ff613c60565b600b545f5b81811015610f8b57826001600160a01b0316600b82815481106135875761358761403b565b5f918252602090912001546001600160a01b03160361364957600b6135ad6001846140f8565b815481106135bd576135bd61403b565b5f91825260209091200154600b80546001600160a01b0390921691839081106135e8576135e861403b565b905f5260205f20015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550600b8054806136245761362461423e565b5f8281526020902081015f1990810180546001600160a01b0319169055019055505050565b600101613562565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa8260405160240161368a91511515815260200190565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915292915050565b6040516313c34b7f60e01b815263ffffffff8086166004830152831660248201525f9081906001600160a01b037f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c16906313c34b7f90604401602060405180830381865afa158015613736573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061375a91906141f0565b90507f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c6001600160a01b0316639cfc058e82888888886040518663ffffffff1660e01b81526004016137af94939291906142b9565b60206040518083038185885af11580156137cb573d5f5f3e3d5ffd5b50505050506040513d601f19601f820116820180604052508101906137f091906141f0565b915094509492505050565b6040516313f2e28560e11b815263ffffffff8086166004830152831660248201525f9081906001600160a01b037f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c16906327e5c50a90604401602060405180830381865afa15801561386f573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061389391906141f0565b90507f000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca6001600160a01b0316634000aea07f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c83898989896040516020016138fd94939291906142b9565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161392a939291906142f7565b6020604051808303815f875af1158015613946573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061396a9190614326565b507f00000000000000000000000002aae1a04f9828517b3007f83f6181900cad910c6001600160a01b031663fc2a88c36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156139c7573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137f091906141f0565b600b546060905f9067ffffffffffffffff811115613a0b57613a0b613d0e565b604051908082528060200260200182016040528015613a34578160200160208202803683370190505b5091505f805b600b54811015613b15575f600a5f600b8481548110613a5b57613a5b61403b565b905f5260205f20015f9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020019081526020015f2060020154600a5f600b8581548110613aaf57613aaf61403b565b5f9182526020808320909101546001600160a01b03168352820192909252604001902060010154613ae09190614028565b9050613aec8184614028565b925082858381518110613b0157613b0161403b565b602090810291909101015250600101613a3a565b5091929050565b5f5f5f90505f60018551613b3091906140f8565b90505b80821015613b9b575f6002613b4884846140f8565b613b5291906141dd565b613b5c9084614028565b905084868281518110613b7157613b7161403b565b60200260200101511015613b9157613b8a816001614028565b9250613b95565b8091505b50613b33565b509392505050565b5f5f60205f8451602086015f885af180613bc2576040513d5f823e3d81fd5b50505f513d91508115613bd9578060011415613be6565b6001600160a01b0384163b155b1561348257604051635274afe760e01b81526001600160a01b0385166004820152602401610c41565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166119ff57604051631afcd79f60e31b815260040160405180910390fd5b6129ea613c0f565b613488613c0f565b828054828255905f5260205f20908101928215613ca1579160200282015b82811115613ca1578251825591602001919060010190613c86565b50611476929150613cc4565b5080545f8255905f5260205f209081019061136b91905b5b80821115611476575f8155600101613cc5565b80356001600160a01b0381168114612d27575f5ffd5b5f60208284031215613cfe575f5ffd5b613d0782613cd8565b9392505050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112613d31575f5ffd5b813567ffffffffffffffff811115613d4b57613d4b613d0e565b8060051b604051601f19603f830116810181811067ffffffffffffffff82111715613d7857613d78613d0e565b604052918252602081850181019290810186841115613d95575f5ffd5b6020860192505b83831015613db4578235815260209283019201613d9c565b5095945050505050565b5f5f5f60608486031215613dd0575f5ffd5b8335925060208401359150604084013567ffffffffffffffff811115613df4575f5ffd5b613e0086828701613d22565b9150509250925092565b5f5f60408385031215613e1b575f5ffd5b82359150602083013567ffffffffffffffff811115613e38575f5ffd5b613e4485828601613d22565b9150509250929050565b5f60208284031215613e5e575f5ffd5b5035919050565b5f5f60408385031215613e76575f5ffd5b823563ffffffff81168114613e89575f5ffd5b9150602083013561ffff81168114613e9f575f5ffd5b809150509250929050565b602080825282518282018190525f918401906040840190835b81811015613eed578351805184526020908101518185015290930192604090920191600101613ec3565b509095945050505050565b5f5f60408385031215613f09575f5ffd5b613f1283613cd8565b946020939093013593505050565b5f5f5f60608486031215613f32575f5ffd5b613f3b84613cd8565b9250613f4960208501613cd8565b9150613f5760408501613cd8565b90509250925092565b5f8151808452602084019350602083015f5b82811015613f90578151865260209586019590910190600101613f72565b5093949350505050565b602081525f613d076020830184613f60565b602080825282518282018190525f918401906040840190835b81811015613eed5783516001600160a01b0316835260209384019390920191600101613fc5565b801515811461136b575f5ffd5b5f60208284031215614009575f5ffd5b8135613d0781613fec565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610b7a57610b7a614014565b634e487b7160e01b5f52603260045260245ffd5b6020808252602c908201527f566572746963616c4149526166666c653a20496e76616c6964207072697a652060408201526b3234b9ba3934b13aba34b7b760a11b606082015260800190565b60208082526039908201527f566572746963616c4149526166666c653a205072697a6520646973747269627560408201527f74696f6e206d7573742062652065786163746c79203130302500000000000000606082015260800190565b81810381811115610b7a57610b7a614014565b848152836020820152608060408201525f6141296080830185613f60565b905082606083015295945050505050565b60208082526027908201527f566572746963616c4149526166666c653a20496e76616c6964207374616b696e60408201526633903a37b5b2b760c91b606082015260800190565b634e487b7160e01b5f52601260045260245ffd5b5f826141a3576141a3614181565b500690565b6020808252818101527f566572746963616c4149526166666c653a20496e76616c696420616d6f756e74604082015260600190565b5f826141eb576141eb614181565b500490565b5f60208284031215614200575f5ffd5b5051919050565b8082028115828204841417610b7a57610b7a614014565b828152604060208201525f6142366040830184613f60565b949350505050565b634e487b7160e01b5f52603160045260245ffd5b5f6001820161426357614263614014565b5060010190565b604081525f61427c6040830185613f60565b90508260208301529392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b63ffffffff8516815261ffff8416602082015263ffffffff83166040820152608060608201525f6142ed608083018461428b565b9695505050505050565b60018060a01b0384168152826020820152606060408201525f61431d606083018461428b565b95945050505050565b5f60208284031215614336575f5ffd5b8151613d0781613fec56fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220089ec3dd1e3798df05d73ee94b014d7a446bab82d14dbf22c67e7f028514640964736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.