This nametag was submitted by Kleros Scout.
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 25 from a total of 44,634 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Create Raffle | 23632343 | 140 days ago | IN | 0 ETH | 0.00050897 | ||||
| Set Winners | 23085166 | 217 days ago | IN | 0 ETH | 0.00022956 | ||||
| Buy Entry | 23085158 | 217 days ago | IN | 0.0038 ETH | 0.00019666 | ||||
| Buy Entry | 23085147 | 217 days ago | IN | 0.0038 ETH | 0.00019475 | ||||
| Buy Entry | 23085047 | 217 days ago | IN | 0.0096 ETH | 0.0001951 | ||||
| Buy Entry | 23085027 | 217 days ago | IN | 0.0038 ETH | 0.00019637 | ||||
| Buy Entry | 23084988 | 217 days ago | IN | 0.0096 ETH | 0.00019541 | ||||
| Buy Entry | 23084921 | 217 days ago | IN | 0.0096 ETH | 0.00019549 | ||||
| Buy Entry | 23084906 | 217 days ago | IN | 0.0096 ETH | 0.000197 | ||||
| Buy Entry | 23084853 | 217 days ago | IN | 0.0038 ETH | 0.00019796 | ||||
| Buy Entry | 23084469 | 217 days ago | IN | 0.0038 ETH | 0.00020413 | ||||
| Buy Entry | 23084458 | 217 days ago | IN | 0.0096 ETH | 0.00020327 | ||||
| Buy Entry | 23084451 | 217 days ago | IN | 0.0038 ETH | 0.00020279 | ||||
| Buy Entry | 23084446 | 217 days ago | IN | 0.0038 ETH | 0.00020369 | ||||
| Buy Entry | 23084392 | 217 days ago | IN | 0.0038 ETH | 0.00020206 | ||||
| Buy Entry | 23084223 | 217 days ago | IN | 0.0038 ETH | 0.00020126 | ||||
| Buy Entry | 23084055 | 217 days ago | IN | 0.038 ETH | 0.00020644 | ||||
| Buy Entry | 23084050 | 217 days ago | IN | 0.11 ETH | 0.00020775 | ||||
| Buy Entry | 23083980 | 217 days ago | IN | 0.019 ETH | 0.00006408 | ||||
| Buy Entry | 23083935 | 217 days ago | IN | 0.038 ETH | 0.00021419 | ||||
| Buy Entry | 23083906 | 217 days ago | IN | 0.0096 ETH | 0.00021098 | ||||
| Buy Entry | 23083906 | 217 days ago | IN | 0.11 ETH | 0.00021098 | ||||
| Buy Entry | 23083632 | 217 days ago | IN | 0.0038 ETH | 0.00023092 | ||||
| Buy Entry | 23083534 | 217 days ago | IN | 0.0038 ETH | 0.00022323 | ||||
| Buy Entry | 23083518 | 217 days ago | IN | 0.0096 ETH | 0.00022914 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 23085170 | 217 days ago | 0.7502 ETH | ||||
| Transfer | 23083386 | 217 days ago | 0.5244 ETH | ||||
| Transfer | 23081595 | 217 days ago | 0.2844 ETH | ||||
| Transfer | 23078012 | 218 days ago | 0.2708 ETH | ||||
| Transfer | 23076219 | 218 days ago | 0.4378 ETH | ||||
| Transfer | 23074429 | 218 days ago | 0.4466 ETH | ||||
| Transfer | 23070852 | 219 days ago | 0.4006 ETH | ||||
| Transfer | 23069055 | 219 days ago | 0.2112 ETH | ||||
| Transfer | 23067265 | 219 days ago | 0.2116 ETH | ||||
| Transfer | 23063689 | 220 days ago | 0.2224 ETH | ||||
| Transfer | 23061893 | 220 days ago | 0.2528 ETH | ||||
| Transfer | 23060104 | 220 days ago | 0.256 ETH | ||||
| Transfer | 23056519 | 221 days ago | 0.3606 ETH | ||||
| Transfer | 23054731 | 221 days ago | 0.5952 ETH | ||||
| Transfer | 23052938 | 221 days ago | 0.272 ETH | ||||
| Transfer | 23049372 | 222 days ago | 0.2814 ETH | ||||
| Transfer | 23047587 | 222 days ago | 0.3696 ETH | ||||
| Transfer | 23045798 | 222 days ago | 0.2158 ETH | ||||
| Transfer | 23042222 | 223 days ago | 0.2976 ETH | ||||
| Transfer | 23040439 | 223 days ago | 0.3146 ETH | ||||
| Transfer | 23038654 | 223 days ago | 0.3506 ETH | ||||
| Transfer | 23035082 | 224 days ago | 0.1926 ETH | ||||
| Transfer | 23033297 | 224 days ago | 0.2492 ETH | ||||
| Transfer | 23031506 | 224 days ago | 0.2666 ETH | ||||
| Transfer | 23020782 | 226 days ago | 0.2002 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
RaffleV3
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 10000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import {AccessControl} from "openzeppelin-contracts/contracts/access/AccessControl.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {IERC721} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";
import {ERC721Holder} from "openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sol";
import {ReentrancyGuard} from "openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol";
import {VRFConsumerBaseV2Plus} from "chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import {VRFV2PlusClient} from "chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol";
import {IUSDC} from "./interfaces/IUSDC.sol";
/// @title RaffleV3 - v1.0.1
/// @notice Host raffles for various tokens in a trustless manner, with a VRF used to select the winner(s).
/// @notice Inspired by LooksRare Raffle V2.
/// @author @pdito - Metawin protocol team (🎰,🎲)
/// ███╗ ███╗███████╗████████╗ █████╗ ██╗ ██╗██╗███╗ ██╗
/// ████╗ ████║╚══════╝╚══██╔══╝██╔══██╗██║ ██║██║████╗ ██║
/// ██╔████╔██║ █████╗ ██║ ███████║██║ █╗ ██║██║██╔██╗ ██║
/// ██║╚██╔╝██║ ╚════╝ ██║ ██╔══██║██║███╗██║██║██║╚██╗██║
/// ██║ ╚═╝ ██║███████╗ ██║ ██║ ██║╚███╔███╔╝██║██║ ╚████║
/// ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝╚═╝ ╚═══╝ ♠♡♣♢
contract RaffleV3 is
AccessControl,
ReentrancyGuard,
VRFConsumerBaseV2Plus,
ERC721Holder
{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* TYPES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
struct Raffle {
// holds raffle data
address owner; // raffle prize staker
RaffleStatus status; // current status of the raffle
uint48 entryCount; // current entry count
uint16 platformPercentage; // percentage of the fees that go to the platform wallet
bool feesClaimed; // whether the fees have been claimed
bool isGated; // whether the raffle has gated entry
uint128 fees; // current fees generated from entry sales
uint128 minimumFees; // minimum amount of fees required before the raffle can be drawn
PriceTier[] priceTiers; // price tiers for the raffle
Prize[] prizes; // prizes for the raffle
Entry[] entries; // entries to the raffle
Winner[] winners; // winners of the raffle
}
struct PriceTier {
// holds raffle entry price data for a given tier. Raffles can have multiple tiers.
uint48 tierId; // index of the price tier
uint48 numEntries; // number of entries allocated by purchasing the tier
uint160 price; // price of the tier in wei
}
struct Prize {
// holds prize data for each prize. Raffles can have multiple prizes.
uint8 prizeId; // index of the prize
bool failed; // if the transfer of the prize failed
TokenType prizeType; // type of the prize
address prizeAddress; // address of the prize (address(0) for ETH)
uint256 prizeNumber; // number of the prize (tokenId for ERC721, size in wei for ETH and ERC20s)
}
struct Winner {
// holds winner data for each winner.
uint8 prizeId; // index of the prize
address player; // address of the winner
uint256 randomNumber; // random number used to select the winner
}
struct Entry {
// holds entry data for each entry.
uint48 currentEntriesLength; // cumulative number of entries in the raffle at the point of purchase (inclusive of the entry itself)
address player; // address of the entry
}
struct TokenConfig {
// holds token data for gating
TokenType tokenType; // type of the token
address tokenAddress; // address of the token
uint88 tokenAmount; // amount of tokens - uint88 to pack into a single slot
}
struct RandomnessRequest {
// holds randomness request data.
bool exists; // whether the request exists, reset to false when a request reset is submitted
bool manual; // whether the request will automatically draw the raffle
bool fulfilled; // whether the request has been fulfilled
uint48 requestTime; // time the request was made
uint80 raffleId; // id of the raffle the request is for
uint256 randomWord; // random number returned by Chainlink
}
struct CreateRaffleCallData {
// parameters required for creating a raffle
uint16 commissionInBasisPoints; // percentage of the funds raised that goes to the platform, in basis points (1% = 100)
uint128 minimumFees; // minimum amount of fees required before the raffle can be drawn
PriceTierCallData[] priceTiers; // price tiers for the raffle
PrizeCallData[] prizes; // prizes for the raffle
}
struct PriceTierCallData {
// parameters required for creating a PriceTier as part of a Raffle
uint48 numEntries; // number of entries allocated by purchasing the tier
uint160 price; // price of the tier in wei
}
struct PrizeCallData {
// parameters required for creating a Prize as part of a Raffle
TokenType prizeType; // type of the prize
address prizeAddress; // address of the prize (address(0) for ETH)
uint256 prizeNumber; // number of the prize (tokenId for ERC721, size in wei for ETH and ERC20s)
}
enum RaffleStatus {
NULL, // null state, to avoid CREATED being default state
CREATED, // the operator creates the raffle
OPEN, // the owner stakes the token for the raffle
RANDOMNESS_REQUESTED, // the operator requests randomness to select the winners
RANDOMNESS_FULFILLED, // the randomness request was fulfilled
DRAWN, // the winners were calculated, prizes can be distributed
PRIZES_DISTRIBUTED, // the prices were distributed to the winners
CLOSED, // the raffle has finished and prizes have been distributed
CANCELLED // the operator has cancelled the raffle
}
enum TokenType {
ETH, // network native token
ERC20, // ERC20 token
ERC721 // ERC721 token
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STATE VARIABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Config
uint16 constant ONE_HUNDRED_PERCENT_IN_BASIS_POINTS = 10_000; // 100% in basis points
uint32 constant MAXIMUM_NUMBER_PRIZES_PER_RAFFLE = 10; // max number of prizes per raffle
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR"); // role for the operator (platform)
address payable public platformWallet; // address of the wallet controlled by the platform that will receive the platform fee
bool public paused = false; // pause for contract
// Chainlink VRF Config
uint256 public s_subscriptionId;
bytes32 public s_keyHash;
uint32 callbackGasLimit = 2500000;
uint16 requestConfirmations = 3;
uint32 numWords = 1;
// Chainlink Data
mapping(uint256 => RandomnessRequest) public randomnessRequests; // maps Chainlink requestId to it's data
mapping(uint256 => uint256) public randomnessLookup; // maps raffleId to it's Chainlink requestId
// USDC Data
IUSDC public immutable usdc; // USDC contract, used to check if a user is blacklisted
// Raffles Data
uint256 public rafflesCount; // number of raffles created
mapping(uint256 => Raffle) public raffles; // maps a raffleId to it's data
mapping(bytes32 => bool) public freeEntryUsed; // maps hash (raffleId, address) => bool to denote free entry usage
mapping(uint256 => TokenConfig) public tokenGates; // maps a raffleId to it's token gate
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Config
event PauseToggled(bool paused);
// Chainlink Events
event RandomnessRequested(uint256 raffleId, uint256 requestId);
event RandomnessReceived(
uint256 raffleId,
uint256 requestId,
uint256[] randomness
);
// Raffle Events
event RaffleCreated(
uint256 indexed raffleId,
uint256 priceTierCount,
uint256 prizeCount
);
event RaffleStarted(
uint256 indexed raffleId,
address indexed owner,
uint256 timestamp
);
event RaffleDrawn(
uint256 indexed raffleId,
address[] winners,
uint256 timestamp
);
event RaffleClosed(
uint256 indexed raffleId,
uint256 amountRaised,
uint48 entries,
uint256 timestamp
);
event RaffleCancelled(
uint256 indexed raffleId,
uint256 amountRaised,
uint256 timestamp
);
event FeesTransferred(
uint256 raffleId,
uint256 platformFees,
uint256 ownerFees
);
event PrizeTransferred(uint256 raffleId, uint8 prizeId, address winner);
event PrizeTransferFailed(uint256 raffleId, uint8 prizeId, address winner);
// Player Events
event EntrySold(
uint256 indexed raffleId,
address indexed buyer,
uint48 entriesCount,
uint256 priceTierId,
uint8 brandId
);
event FreeEntry(
uint256 indexed raffleId,
address[] buyer,
uint256 amount,
uint48 entriesCount,
uint8 brandId
);
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error FeesAlreadyClaimed();
error GatingTokenBalanceTooLow();
error InvalidCommissionPercentage();
error InvalidFeeSupplied();
error InvalidFreeEntry();
error InvalidGatingToken();
error InvalidNativeTokenAmountStaked();
error InvalidPriceTier();
error InvalidPriceTiersCount();
error InvalidPrize();
error InvalidPrizesCount();
error InvalidStatus();
error NativeTokenTransferFailed();
error NotEnoughFeesRaised();
error Paused();
error RandomnessRequestAlreadyFulfilled();
error RandomnessRequestDoesNotExist();
error RandomnessRequestResetTooEarly();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice the contract constructor
/// @param _platformWallet address of the wallet controlled by the platform that will receive the platform fee
/// @param _usdc address of the USDC contract
/// @param _subscriptionId subscription id for the Chainlink VRF
/// @param _vrfCoordinator address of the Chainlink VRF coordinator
/// @param _keyHash key hash for the Chainlink VRF
constructor(
address _platformWallet,
address _usdc,
uint256 _subscriptionId,
address _vrfCoordinator,
bytes32 _keyHash
) VRFConsumerBaseV2Plus(_vrfCoordinator) {
platformWallet = payable(_platformWallet);
usdc = IUSDC(_usdc);
s_subscriptionId = _subscriptionId;
s_keyHash = _keyHash;
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(OPERATOR_ROLE, msg.sender);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice modifier to check if the contract is not paused
modifier whenNotPaused() {
if (paused) revert Paused();
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EXTERNAL */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice creates a raffle. A raffle can have multiple prizes, across the native token (ETH), ERC20s & ERC721s.
/// @dev if the raffle has gated entry, the setRaffleGate must be called.
/// @param _params parameters required for creating a raffle
/// @return raffleId the id of the raffle created
function createRaffle(
CreateRaffleCallData calldata _params
) external onlyRole(OPERATOR_ROLE) nonReentrant returns (uint256) {
uint256 priceTiersCount = _params.priceTiers.length;
uint256 prizesCount = _params.prizes.length;
if (priceTiersCount == 0) {
revert InvalidPriceTiersCount();
}
if (
prizesCount == 0 || prizesCount > MAXIMUM_NUMBER_PRIZES_PER_RAFFLE
) {
revert InvalidPrizesCount();
}
if (_params.commissionInBasisPoints > 10000) {
revert InvalidCommissionPercentage();
}
uint256 raffleId = ++rafflesCount;
Raffle storage raffle = raffles[raffleId];
raffle.status = RaffleStatus.CREATED;
raffle.platformPercentage = _params.commissionInBasisPoints;
raffle.minimumFees = _params.minimumFees;
for (uint256 i; i < prizesCount; ++i) {
PrizeCallData memory prize = _params.prizes[i];
if (
prize.prizeType > TokenType.ERC721 ||
(prize.prizeNumber == 0 &&
prize.prizeType != TokenType.ERC721) ||
(prize.prizeType != TokenType.ETH &&
prize.prizeAddress == address(0))
) revert InvalidPrize();
Prize storage newPrize = raffle.prizes.push();
newPrize.prizeId = uint8(i);
newPrize.prizeType = prize.prizeType;
newPrize.prizeAddress = prize.prizeAddress;
newPrize.prizeNumber = prize.prizeNumber;
}
for (uint256 j; j < priceTiersCount; ++j) {
PriceTierCallData memory priceTier = _params.priceTiers[j];
if (priceTier.numEntries == 0 || (priceTier.price == 0 && j != 0))
revert InvalidPriceTier();
if (j > 0 && (priceTier.price <= raffle.priceTiers[j - 1].price))
revert InvalidPriceTier();
PriceTier storage newPriceTier = raffle.priceTiers.push();
newPriceTier.tierId = uint48(j);
newPriceTier.numEntries = priceTier.numEntries;
newPriceTier.price = priceTier.price;
}
// prime the entries array to reduce excessive gas that would typically apply for the first entrant
Entry memory entry = Entry({
player: address(0),
currentEntriesLength: 0
});
raffle.entries.push(entry);
delete raffle.entries[0];
emit RaffleCreated(raffleId, priceTiersCount, prizesCount);
return raffleId;
}
/// @notice everyone can call this method and will receive all fees ex-platform fees
/// @param _raffleId the id of the raffle
/// @dev the caller must have approved the raffle contract to spend any ERC tokens
function stake(uint256 _raffleId) external payable nonReentrant {
Raffle storage raffle = raffles[_raffleId];
uint256 numOfPrizes = raffle.prizes.length;
if (raffle.status != RaffleStatus.CREATED) revert InvalidStatus();
raffle.owner = msg.sender;
uint256 counterETH = 0;
for (uint256 i; i < numOfPrizes; ++i) {
Prize memory prize = raffle.prizes[i];
if (prize.prizeType == TokenType.ETH) {
counterETH += prize.prizeNumber;
} else if (prize.prizeType == TokenType.ERC20) {
IERC20(prize.prizeAddress).transferFrom(
msg.sender,
address(this),
prize.prizeNumber
);
} else if (prize.prizeType == TokenType.ERC721) {
IERC721(prize.prizeAddress).safeTransferFrom(
msg.sender,
address(this),
prize.prizeNumber
);
} else {
revert InvalidPrize();
}
}
raffle.status = RaffleStatus.OPEN;
if (counterETH != msg.value) revert InvalidNativeTokenAmountStaked();
emit RaffleStarted(_raffleId, msg.sender, block.timestamp);
}
/// @notice adds a token gate to the raffle
/// @param _raffleId the id of the raffle
/// @param _tokenType the type of token
/// @param _tokenAddress the address of the token
/// @param _tokenAmount the amount of tokens than must be held
function setTokenGate(
uint256 _raffleId,
TokenType _tokenType,
address _tokenAddress,
uint88 _tokenAmount
) external onlyRole(OPERATOR_ROLE) {
Raffle storage raffle = raffles[_raffleId];
raffle.isGated = true;
if (_tokenAddress == address(0) || _tokenAmount == 0)
revert InvalidGatingToken();
TokenConfig storage tokenGate = tokenGates[_raffleId];
tokenGate.tokenType = _tokenType;
tokenGate.tokenAddress = _tokenAddress;
tokenGate.tokenAmount = _tokenAmount;
}
/// @notice removes a token gate from the raffle
/// @param _raffleId the id of the raffle
function removeTokenGate(
uint256 _raffleId
) external onlyRole(OPERATOR_ROLE) {
Raffle storage raffle = raffles[_raffleId];
raffle.isGated = false;
delete tokenGates[_raffleId];
}
/// @notice buy an entry in the raffle
/// @param _raffleId the id of the raffle
/// @param _priceTierIndex the index of the price tier in the price tiers array
/// @param _brandId the brand id of source site
function buyEntry(
uint256 _raffleId,
uint256 _priceTierIndex,
uint8 _brandId
) external payable whenNotPaused nonReentrant {
Raffle storage raffle = raffles[_raffleId];
if (raffle.status != RaffleStatus.OPEN) revert InvalidStatus();
if (raffle.isGated) _validateTokenConfig(_raffleId, msg.sender);
uint256 priceTierCount = raffle.priceTiers.length;
if (_priceTierIndex >= priceTierCount) revert InvalidPriceTier();
PriceTier memory priceTier = raffle.priceTiers[_priceTierIndex];
if (msg.value != priceTier.price) revert InvalidFeeSupplied();
if (priceTier.price == 0) {
bytes32 key = keccak256(abi.encode(msg.sender, _raffleId));
if (freeEntryUsed[key]) revert InvalidFreeEntry();
freeEntryUsed[key] = true;
}
// if it's the first entry, overwrite placeholder - avoids first entrant paying excessive gas
if (raffle.entryCount == 0) {
raffle.entries[0].player = msg.sender;
raffle.entries[0].currentEntriesLength = priceTier.numEntries;
} else {
Entry memory entryBought = Entry({
player: msg.sender,
currentEntriesLength: raffle.entryCount + priceTier.numEntries
});
raffle.entries.push(entryBought);
}
raffle.fees += uint128(msg.value);
raffle.entryCount += priceTier.numEntries;
emit EntrySold(
_raffleId,
msg.sender,
raffle.entryCount,
_priceTierIndex,
_brandId
);
}
/// @notice allows the operator to distribute free entries to users
/// @param _raffleId the id of the raffle
/// @param _addresses array of addresses receiving a free entry
/// @param _brandId the brand id of source site
function giveBatchEntriesForFree(
uint256 _raffleId,
address[] memory _addresses,
uint8 _brandId
) external onlyRole(OPERATOR_ROLE) whenNotPaused nonReentrant {
Raffle storage raffle = raffles[_raffleId];
if (raffle.status != RaffleStatus.OPEN) revert InvalidStatus();
uint256 addressesLength = _addresses.length;
uint48 validPlayersCount = 0;
for (uint256 i; i < addressesLength; ++i) {
address player = _addresses[i];
Entry memory entry = Entry({
player: player,
currentEntriesLength: uint48(raffle.entryCount + i + 1)
});
// First entry replaces placeholder
if (raffle.entryCount == 0 && i == 0) {
raffle.entries[0] = entry;
} else {
raffle.entries.push(entry);
}
unchecked {
++validPlayersCount;
}
}
raffle.entryCount += validPlayersCount;
emit FreeEntry(
_raffleId,
_addresses,
addressesLength,
raffle.entryCount,
_brandId
);
}
/// @notice enables the operator to end the raffle and submit a randomness request to draw winners
/// @param _raffleId id of the raffle
/// @param _manual if the post randomness draw and prize distribution should be manual instead of automatic
function setWinners(
uint256 _raffleId,
bool _manual
) external onlyRole(OPERATOR_ROLE) whenNotPaused nonReentrant {
Raffle storage raffle = raffles[_raffleId];
if (raffle.status != RaffleStatus.OPEN) revert InvalidStatus();
if (raffle.fees < raffle.minimumFees) revert NotEnoughFeesRaised();
_requestRandomness(_raffleId, _manual);
}
/// @notice enables the operator to draw winners for a raffle after the randomness request has been fulfilled
/// @param _raffleId id of the raffle
function drawWinners(
uint256 _raffleId
) external onlyRole(OPERATOR_ROLE) whenNotPaused nonReentrant {
Raffle storage raffle = raffles[_raffleId];
if (raffle.status != RaffleStatus.RANDOMNESS_FULFILLED)
revert InvalidStatus();
_drawWinners(_raffleId);
}
/// @notice enables the operator to transfer prizes and fees after winners have been drawn
/// @param _raffleId id of the raffle
function transferPrizes(
uint256 _raffleId
) external onlyRole(OPERATOR_ROLE) whenNotPaused nonReentrant {
Raffle storage raffle = raffles[_raffleId];
if (raffle.status != RaffleStatus.DRAWN) revert InvalidStatus();
_transferPrizes(_raffleId);
}
/// @notice recover prizes where transfers failed
/// @param _raffleId id of the raffle
/// @param _prizeId id of the prize
function recoverPrize(
uint256 _raffleId,
uint8 _prizeId
) external onlyRole(OPERATOR_ROLE) whenNotPaused nonReentrant {
Raffle storage raffle = raffles[_raffleId];
Prize memory prize = raffle.prizes[_prizeId];
if (prize.failed) {
_transferPrize(_raffleId, prize, raffle.owner);
}
}
/// @notice enables the operator to transfer fees after winners have been drawn
/// @param _raffleId id of the raffle
function transferFees(
uint256 _raffleId
) external onlyRole(OPERATOR_ROLE) whenNotPaused nonReentrant {
Raffle storage raffle = raffles[_raffleId];
if (raffle.status != RaffleStatus.PRIZES_DISTRIBUTED)
revert InvalidStatus();
_transferFees(_raffleId);
}
/// @notice If a randomness request fails to be fulfilled, the operator can reset the raffle after 5 minutes
/// @param _raffleId id of the raffle
function resetRandomnessRequest(
uint256 _raffleId
) external onlyRole(OPERATOR_ROLE) nonReentrant {
Raffle storage raffle = raffles[_raffleId];
RandomnessRequest storage request = randomnessRequests[
randomnessLookup[_raffleId]
];
if (!request.exists) revert RandomnessRequestDoesNotExist();
if (request.fulfilled) revert RandomnessRequestAlreadyFulfilled();
if (raffle.status != RaffleStatus.RANDOMNESS_REQUESTED)
revert InvalidStatus();
if (request.requestTime + 5 minutes > block.timestamp)
revert RandomnessRequestResetTooEarly();
request.exists = false;
raffle.status = RaffleStatus.OPEN;
}
/// @notice the operator can cancel the raffle, the prizes are sent back to the owner
/// @notice fees are sent to the platform wallet for redistribution to the players
/// @dev for fairness, raffles cannot be cancelled once winners have been drawn
/// @param _raffleId Id of the raffle
function cancelRaffle(
uint256 _raffleId
) external onlyRole(OPERATOR_ROLE) nonReentrant {
Raffle storage raffle = raffles[_raffleId];
if (
raffle.status == RaffleStatus.DRAWN ||
raffle.status == RaffleStatus.PRIZES_DISTRIBUTED ||
raffle.status == RaffleStatus.CLOSED ||
raffle.status == RaffleStatus.CANCELLED
) revert InvalidStatus();
// only transfer prizes and fees if the raffle was opened at any point
if (
raffle.status == RaffleStatus.OPEN ||
raffle.status == RaffleStatus.RANDOMNESS_REQUESTED
) {
for (uint256 i; i < raffle.prizes.length; ++i) {
Prize memory prize = raffle.prizes[i];
_transferPrize(_raffleId, prize, raffle.owner);
}
(bool sentPlatform, ) = platformWallet.call{value: raffle.fees}("");
if (!sentPlatform) revert NativeTokenTransferFailed();
raffle.feesClaimed = true;
}
raffle.status = RaffleStatus.CANCELLED;
emit RaffleCancelled(_raffleId, raffle.fees, block.timestamp);
}
/// @notice change the subscription id for Chainlink
/// @param _id the new id
function setSubscriptionId(
uint256 _id
) external onlyRole(DEFAULT_ADMIN_ROLE) {
s_subscriptionId = _id;
}
/// @notice change the keyhash for the Chainlink VRF2 consumer
/// @param _keyHash the new keyhash
function setKeyHash(
bytes32 _keyHash
) external onlyRole(DEFAULT_ADMIN_ROLE) {
s_keyHash = _keyHash;
}
/// @notice change the address of the platform wallet, the wallet that will receive platform fees when the raffle is closed
/// @notice this wallet also receives the fees if the raffle is cancelled
/// @param _newAddress new address of the platform
function setPlatformAddress(
address payable _newAddress
) external onlyRole(DEFAULT_ADMIN_ROLE) {
platformWallet = _newAddress;
}
/// @notice toggles contract pause
/// @notice when paused entries cannot be bought, raffles cannot be drawn and prizes cannot be transferred
function togglePaused() external onlyRole(OPERATOR_ROLE) {
paused = !paused;
emit PauseToggled(paused);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice validates if the sender has the required balance of the gating token
/// @param _raffleId id of the raffle
function _validateTokenConfig(
uint256 _raffleId,
address _player
) internal view {
TokenConfig memory tokenGate = tokenGates[_raffleId];
if (tokenGate.tokenType == TokenType.ERC20) {
IERC20 token = IERC20(tokenGate.tokenAddress);
if (token.balanceOf(_player) < tokenGate.tokenAmount)
revert GatingTokenBalanceTooLow();
} else if (tokenGate.tokenType == TokenType.ERC721) {
IERC721 token = IERC721(tokenGate.tokenAddress);
if (token.balanceOf(_player) < tokenGate.tokenAmount)
revert GatingTokenBalanceTooLow();
}
}
/// @notice requests randomness from the Chainlink VRF2 consumer
/// @param _raffleId id of the raffle
/// @param _manual if the post randomness draw and prize distribution should be manual instead of automatic
function _requestRandomness(uint256 _raffleId, bool _manual) internal {
Raffle storage raffle = raffles[_raffleId];
raffle.status = RaffleStatus.RANDOMNESS_REQUESTED;
uint256 requestId = s_vrfCoordinator.requestRandomWords(
VRFV2PlusClient.RandomWordsRequest({
keyHash: s_keyHash,
subId: s_subscriptionId,
requestConfirmations: requestConfirmations,
callbackGasLimit: callbackGasLimit,
numWords: numWords,
// Set nativePayment to true to pay for VRF requests with Sepolia ETH instead of LINK
extraArgs: VRFV2PlusClient._argsToBytes(
VRFV2PlusClient.ExtraArgsV1({nativePayment: true})
)
})
);
randomnessRequests[requestId].exists = true;
randomnessRequests[requestId].manual = _manual;
randomnessRequests[requestId].requestTime = uint48(block.timestamp);
randomnessRequests[requestId].raffleId = uint80(_raffleId);
randomnessLookup[_raffleId] = requestId;
emit RandomnessRequested(_raffleId, requestId);
}
/// @notice called by the Chainlink VRF2 consumer when a randomness request is fulfilled
/// @param _requestId id of the randomness request
/// @param _randomWords array of random words
function fulfillRandomWords(
uint256 _requestId,
uint256[] calldata _randomWords
) internal override {
RandomnessRequest storage request = randomnessRequests[_requestId];
if (request.exists) {
uint256 raffleId = request.raffleId;
Raffle storage raffle = raffles[raffleId];
emit RandomnessReceived(raffleId, _requestId, _randomWords);
if (raffle.status == RaffleStatus.RANDOMNESS_REQUESTED) {
request.fulfilled = true;
request.randomWord = _randomWords[0];
raffle.status = RaffleStatus.RANDOMNESS_FULFILLED;
if (!request.manual && !paused) {
_drawWinners(raffleId);
_transferPrizes(raffleId);
_transferFees(raffleId);
}
}
}
}
/// @notice draws winners for a raffle from the randomness received
/// @notice we use resampling via a bitmap to ensure that no duplicate winners are drawn
/// @notice each prize is drawn from randomness based on the hash of the previous random number
/// @param _raffleId id of the raffle
function _drawWinners(uint256 _raffleId) internal {
uint256 requestId = randomnessLookup[_raffleId];
RandomnessRequest memory randomnessRequest = randomnessRequests[
requestId
];
if (!randomnessRequest.exists) {
revert RandomnessRequestDoesNotExist();
}
Raffle storage raffle = raffles[_raffleId];
if (raffle.status != RaffleStatus.RANDOMNESS_FULFILLED) {
revert InvalidStatus();
}
uint256 prizesCount = raffle.prizes.length;
uint256 entriesCount = raffle.entries.length;
uint256 currentEntryIndex = uint256(
raffle.entries[entriesCount - 1].currentEntriesLength
);
uint256[] memory winningEntriesBitmap = new uint256[](
(currentEntryIndex >> 8) + 1
);
uint256 randomWord = randomnessRequest.randomWord;
uint256 winningEntry;
address[] memory winners = new address[](prizesCount);
uint256 winnerSelectionAttempts = 0;
for (uint256 i; i < prizesCount; ) {
Prize memory prize = raffle.prizes[i];
if (winnerSelectionAttempts >= currentEntryIndex) {
winners[i] = raffle.owner;
raffle.winners.push(
Winner({
player: raffle.owner,
randomNumber: randomWord,
prizeId: prize.prizeId
})
);
unchecked {
++i;
}
randomWord = uint256(keccak256(abi.encodePacked(randomWord)));
continue;
}
(
randomWord,
winningEntry,
winningEntriesBitmap
) = _searchForWinningEntryUntilThereIsNotADuplicate(
randomWord,
currentEntryIndex,
winningEntriesBitmap
);
winnerSelectionAttempts++;
address winner = raffle
.entries[_findUpperBound(raffle.entries, winningEntry)]
.player;
// Prevent USDC winners from being blacklisted
if (
prize.prizeType == TokenType.ERC20 &&
prize.prizeAddress == address(usdc) &&
usdc.isBlacklisted(winner)
) {
while (usdc.isBlacklisted(winner)) {
if (winnerSelectionAttempts >= currentEntryIndex) {
winner = raffle.owner;
break;
}
randomWord = uint256(
keccak256(abi.encodePacked(randomWord))
);
(
randomWord,
winningEntry,
winningEntriesBitmap
) = _searchForWinningEntryUntilThereIsNotADuplicate(
randomWord,
currentEntryIndex,
winningEntriesBitmap
);
winnerSelectionAttempts++;
winner = raffle
.entries[_findUpperBound(raffle.entries, winningEntry)]
.player;
}
}
winners[i] = winner;
raffle.winners.push(
Winner({
player: winner,
randomNumber: randomWord,
prizeId: prize.prizeId
})
);
randomWord = uint256(keccak256(abi.encodePacked(randomWord)));
unchecked {
++i;
}
}
raffle.status = RaffleStatus.DRAWN;
emit RaffleDrawn(_raffleId, winners, block.timestamp);
}
/// @notice transfers prizes and fees to the winners, the owner and the platform
/// @param _raffleId id of the raffle
function _transferPrizes(uint256 _raffleId) internal {
Raffle storage raffle = raffles[_raffleId];
if (raffle.status != RaffleStatus.DRAWN) {
revert InvalidStatus();
}
for (uint256 i; i < raffle.winners.length; i++) {
Winner memory winner = raffle.winners[i];
if (winner.player == address(0)) {
_transferPrize(
_raffleId,
raffle.prizes[winner.prizeId],
raffle.owner
);
} else {
_transferPrize(
_raffleId,
raffle.prizes[winner.prizeId],
winner.player
);
}
}
raffle.status = RaffleStatus.PRIZES_DISTRIBUTED;
}
/// @notice transfers fees to the platform and owner
/// @param _raffleId id of the raffle
function _transferFees(uint256 _raffleId) internal {
Raffle storage raffle = raffles[_raffleId];
if (raffle.feesClaimed) revert FeesAlreadyClaimed();
uint256 platformFees = (raffle.fees * raffle.platformPercentage) /
ONE_HUNDRED_PERCENT_IN_BASIS_POINTS;
uint256 ownerFees = raffle.fees - platformFees;
(bool sentPlatform, ) = platformWallet.call{value: platformFees}("");
(bool sentOwner, ) = raffle.owner.call{value: ownerFees}("");
if (!sentPlatform || !sentOwner) revert NativeTokenTransferFailed();
raffle.feesClaimed = true;
raffle.status = RaffleStatus.CLOSED;
emit FeesTransferred(_raffleId, platformFees, ownerFees);
}
/// @notice transfers a prize to a winner
/// @param _prize prize to transfer
/// @param _winner address of the winner
function _transferPrize(
uint256 _raffleId,
Prize memory _prize,
address _winner
) internal {
bool success = false;
if (_prize.prizeType == TokenType.ETH && _prize.prizeNumber > 0) {
(success, ) = _winner.call{value: _prize.prizeNumber}("");
if (!success) {
Raffle storage raffle = raffles[_raffleId];
raffle.prizes[_prize.prizeId].failed = true;
}
} else if (_prize.prizeType == TokenType.ERC20) {
try
IERC20(_prize.prizeAddress).transfer(
_winner,
_prize.prizeNumber
)
{
success = true;
} catch {
Raffle storage raffle = raffles[_raffleId];
raffle.prizes[_prize.prizeId].failed = true;
}
} else if (_prize.prizeType == TokenType.ERC721) {
try
IERC721(_prize.prizeAddress).safeTransferFrom(
address(this),
_winner,
_prize.prizeNumber
)
{
success = true;
} catch {
Raffle storage raffle = raffles[_raffleId];
raffle.prizes[_prize.prizeId].failed = true;
}
}
if (success) {
emit PrizeTransferred(_raffleId, _prize.prizeId, _winner);
} else {
emit PrizeTransferFailed(_raffleId, _prize.prizeId, _winner);
}
}
/// @notice searches for a winning entry until there is not a duplicate
/// @param randomWord current random word
/// @param totalEntries total entries
/// @param winningEntriesBitmap bitmap of winning entries
/// @return winningEntry winning entry
/// @return randomWord updated random word
/// @return winningEntriesBitmap updated bitmap of winning entries
function _searchForWinningEntryUntilThereIsNotADuplicate(
uint256 randomWord,
uint256 totalEntries,
uint256[] memory winningEntriesBitmap
) internal pure returns (uint256, uint256, uint256[] memory) {
uint256 winningEntry = (randomWord % totalEntries) + 1;
uint256 bucket = winningEntry >> 8;
uint256 mask = 1 << (winningEntry & 0xff);
while (winningEntriesBitmap[bucket] & mask != 0) {
randomWord = uint256(keccak256(abi.encodePacked(randomWord)));
winningEntry = (randomWord % totalEntries) + 1;
bucket = winningEntry >> 8;
mask = 1 << (winningEntry & 0xff);
}
winningEntriesBitmap[bucket] |= mask;
return (randomWord, winningEntry, winningEntriesBitmap);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice adds two numbers without checking for overflow
/// @param a first number
/// @param b second number
/// @return uint256 sum of the two numbers
function _unsafeAdd(uint256 a, uint256 b) private pure returns (uint256) {
unchecked {
return a + b;
}
}
/// @notice subtracts two numbers without checking for underflow
/// @param a first number
/// @param b second number
/// @return uint256 difference of the two numbers
function _unsafeSubtract(
uint256 a,
uint256 b
) private pure returns (uint256) {
unchecked {
return a - b;
}
}
/**
* @notice Searches a sorted `array` and returns the first index that contains
* a value greater or equal to the element.
* @dev If no such index exists (i.e. all values in the array are strictly less than `element`),
* the array length is returned.
* Time complexity O(log n).
*
* `array` is expected to be sorted in ascending order, and to contain no
* repeated elements.
*/
/// @notice From OpenZeppelin, modified to take a memory array instead of a storage array
/// @dev Deprecated in favour of lowerBound, which supports duplicate elements. Not relevant for this project.
/// @param array the sorted array to search
/// @param element the element to search for
/// @return index index of the first element greater than or equal to the element
function _findUpperBound(
Entry[] storage array,
uint256 element
) private view returns (uint256) {
if (array.length == 0) {
return 0;
}
uint256 low = 0;
uint256 high = array.length;
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds down (it does integer division with truncation).
if (array[mid].currentEntriesLength > element) {
high = mid;
} else {
unchecked {
low = mid + 1;
}
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && array[low - 1].currentEntriesLength == element) {
unchecked {
return low - 1;
}
} else {
return low;
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEW */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Gets a raffle with all its data
/// @param _raffleId id of the raffle
/// @return raffle Raffle struct
function getRaffle(
uint256 _raffleId
) external view returns (Raffle memory raffle) {
raffle = raffles[_raffleId];
}
/// @notice Gets winners of a raffle
/// @param _raffleId id of the raffle
/// @return winners array of winners
function getWinners(
uint256 _raffleId
) external view returns (Winner[] memory) {
return raffles[_raffleId].winners;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
// solhint-disable-next-line gas-custom-errors
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/// @notice Allows an owner to begin transferring ownership to a new address.
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
/// @notice Allows an ownership transfer to be completed by the recipient.
function acceptOwnership() external override {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/// @notice Get the current owner
function owner() public view override returns (address) {
return s_owner;
}
/// @notice validate, transfer ownership, and emit relevant events
function _transferOwnership(address to) private {
// solhint-disable-next-line gas-custom-errors
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/// @notice validate access
function _validateOwnership() internal view {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_owner, "Only callable by owner");
}
/// @notice Reverts if called by anyone other than the contract owner.
modifier onlyOwner() {
_validateOwnership();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {VRFV2PlusClient} from "../libraries/VRFV2PlusClient.sol";
import {IVRFSubscriptionV2Plus} from "./IVRFSubscriptionV2Plus.sol";
// Interface that enables consumers of VRFCoordinatorV2Plus to be future-proof for upgrades
// This interface is supported by subsequent versions of VRFCoordinatorV2Plus
interface IVRFCoordinatorV2Plus is IVRFSubscriptionV2Plus {
/**
* @notice Request a set of random words.
* @param req - a struct containing following fields for randomness request:
* keyHash - Corresponds to a particular oracle job which uses
* that key for generating the VRF proof. Different keyHash's have different gas price
* ceilings, so you can select a specific one to bound your maximum per request cost.
* subId - The ID of the VRF subscription. Must be funded
* with the minimum subscription balance required for the selected keyHash.
* requestConfirmations - How many blocks you'd like the
* oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
* for why you may want to request more. The acceptable range is
* [minimumRequestBlockConfirmations, 200].
* callbackGasLimit - How much gas you'd like to receive in your
* fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
* may be slightly less than this amount because of gas used calling the function
* (argument decoding etc.), so you may need to request slightly more than you expect
* to have inside fulfillRandomWords. The acceptable range is
* [0, maxGasLimit]
* numWords - The number of uint256 random values you'd like to receive
* in your fulfillRandomWords callback. Note these numbers are expanded in a
* secure way by the VRFCoordinator from a single random value supplied by the oracle.
* extraArgs - abi-encoded extra args
* @return requestId - A unique identifier of the request. Can be used to match
* a request to a response in fulfillRandomWords.
*/
function requestRandomWords(VRFV2PlusClient.RandomWordsRequest calldata req) external returns (uint256 requestId);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice The IVRFMigratableConsumerV2Plus interface defines the
/// @notice method required to be implemented by all V2Plus consumers.
/// @dev This interface is designed to be used in VRFConsumerBaseV2Plus.
interface IVRFMigratableConsumerV2Plus {
event CoordinatorSet(address vrfCoordinator);
/// @notice Sets the VRF Coordinator address
/// @notice This method should only be callable by the coordinator or contract owner
function setCoordinator(address vrfCoordinator) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice The IVRFSubscriptionV2Plus interface defines the subscription
/// @notice related methods implemented by the V2Plus coordinator.
interface IVRFSubscriptionV2Plus {
/**
* @notice Add a consumer to a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - New consumer which can use the subscription
*/
function addConsumer(uint256 subId, address consumer) external;
/**
* @notice Remove a consumer from a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - Consumer to remove from the subscription
*/
function removeConsumer(uint256 subId, address consumer) external;
/**
* @notice Cancel a subscription
* @param subId - ID of the subscription
* @param to - Where to send the remaining LINK to
*/
function cancelSubscription(uint256 subId, address to) external;
/**
* @notice Accept subscription owner transfer.
* @param subId - ID of the subscription
* @dev will revert if original owner of subId has
* not requested that msg.sender become the new owner.
*/
function acceptSubscriptionOwnerTransfer(uint256 subId) external;
/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @param newOwner - proposed new owner of the subscription
*/
function requestSubscriptionOwnerTransfer(uint256 subId, address newOwner) external;
/**
* @notice Create a VRF subscription.
* @return subId - A unique subscription id.
* @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
* @dev Note to fund the subscription with LINK, use transferAndCall. For example
* @dev LINKTOKEN.transferAndCall(
* @dev address(COORDINATOR),
* @dev amount,
* @dev abi.encode(subId));
* @dev Note to fund the subscription with Native, use fundSubscriptionWithNative. Be sure
* @dev to send Native with the call, for example:
* @dev COORDINATOR.fundSubscriptionWithNative{value: amount}(subId);
*/
function createSubscription() external returns (uint256 subId);
/**
* @notice Get a VRF subscription.
* @param subId - ID of the subscription
* @return balance - LINK balance of the subscription in juels.
* @return nativeBalance - native balance of the subscription in wei.
* @return reqCount - Requests count of subscription.
* @return owner - owner of the subscription.
* @return consumers - list of consumer address which are able to use this subscription.
*/
function getSubscription(
uint256 subId
)
external
view
returns (uint96 balance, uint96 nativeBalance, uint64 reqCount, address owner, address[] memory consumers);
/*
* @notice Check to see if there exists a request commitment consumers
* for all consumers and keyhashes for a given sub.
* @param subId - ID of the subscription
* @return true if there exists at least one unfulfilled request for the subscription, false
* otherwise.
*/
function pendingRequestExists(uint256 subId) external view returns (bool);
/**
* @notice Paginate through all active VRF subscriptions.
* @param startIndex index of the subscription to start from
* @param maxCount maximum number of subscriptions to return, 0 to return all
* @dev the order of IDs in the list is **not guaranteed**, therefore, if making successive calls, one
* @dev should consider keeping the blockheight constant to ensure a holistic picture of the contract state
*/
function getActiveSubscriptionIds(uint256 startIndex, uint256 maxCount) external view returns (uint256[] memory);
/**
* @notice Fund a subscription with native.
* @param subId - ID of the subscription
* @notice This method expects msg.value to be greater than or equal to 0.
*/
function fundSubscriptionWithNative(uint256 subId) external payable;
}// 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
pragma solidity ^0.8.4;
import {IVRFCoordinatorV2Plus} from "./interfaces/IVRFCoordinatorV2Plus.sol";
import {IVRFMigratableConsumerV2Plus} from "./interfaces/IVRFMigratableConsumerV2Plus.sol";
import {ConfirmedOwner} from "../../shared/access/ConfirmedOwner.sol";
/** ****************************************************************************
* @notice Interface for contracts using VRF randomness
* *****************************************************************************
* @dev PURPOSE
*
* @dev Reggie the Random Oracle (not his real job) wants to provide randomness
* @dev to Vera the verifier in such a way that Vera can be sure he's not
* @dev making his output up to suit himself. Reggie provides Vera a public key
* @dev to which he knows the secret key. Each time Vera provides a seed to
* @dev Reggie, he gives back a value which is computed completely
* @dev deterministically from the seed and the secret key.
*
* @dev Reggie provides a proof by which Vera can verify that the output was
* @dev correctly computed once Reggie tells it to her, but without that proof,
* @dev the output is indistinguishable to her from a uniform random sample
* @dev from the output space.
*
* @dev The purpose of this contract is to make it easy for unrelated contracts
* @dev to talk to Vera the verifier about the work Reggie is doing, to provide
* @dev simple access to a verifiable source of randomness. It ensures 2 things:
* @dev 1. The fulfillment came from the VRFCoordinatorV2Plus.
* @dev 2. The consumer contract implements fulfillRandomWords.
* *****************************************************************************
* @dev USAGE
*
* @dev Calling contracts must inherit from VRFConsumerBaseV2Plus, and can
* @dev initialize VRFConsumerBaseV2Plus's attributes in their constructor as
* @dev shown:
*
* @dev contract VRFConsumerV2Plus is VRFConsumerBaseV2Plus {
* @dev constructor(<other arguments>, address _vrfCoordinator, address _subOwner)
* @dev VRFConsumerBaseV2Plus(_vrfCoordinator, _subOwner) public {
* @dev <initialization with other arguments goes here>
* @dev }
* @dev }
*
* @dev The oracle will have given you an ID for the VRF keypair they have
* @dev committed to (let's call it keyHash). Create a subscription, fund it
* @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface
* @dev subscription management functions).
* @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations,
* @dev callbackGasLimit, numWords, extraArgs),
* @dev see (IVRFCoordinatorV2Plus for a description of the arguments).
*
* @dev Once the VRFCoordinatorV2Plus has received and validated the oracle's response
* @dev to your request, it will call your contract's fulfillRandomWords method.
*
* @dev The randomness argument to fulfillRandomWords is a set of random words
* @dev generated from your requestId and the blockHash of the request.
*
* @dev If your contract could have concurrent requests open, you can use the
* @dev requestId returned from requestRandomWords to track which response is associated
* @dev with which randomness request.
* @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind,
* @dev if your contract could have multiple requests in flight simultaneously.
*
* @dev Colliding `requestId`s are cryptographically impossible as long as seeds
* @dev differ.
*
* *****************************************************************************
* @dev SECURITY CONSIDERATIONS
*
* @dev A method with the ability to call your fulfillRandomness method directly
* @dev could spoof a VRF response with any random value, so it's critical that
* @dev it cannot be directly called by anything other than this base contract
* @dev (specifically, by the VRFConsumerBaseV2Plus.rawFulfillRandomness method).
*
* @dev For your users to trust that your contract's random behavior is free
* @dev from malicious interference, it's best if you can write it so that all
* @dev behaviors implied by a VRF response are executed *during* your
* @dev fulfillRandomness method. If your contract must store the response (or
* @dev anything derived from it) and use it later, you must ensure that any
* @dev user-significant behavior which depends on that stored value cannot be
* @dev manipulated by a subsequent VRF request.
*
* @dev Similarly, both miners and the VRF oracle itself have some influence
* @dev over the order in which VRF responses appear on the blockchain, so if
* @dev your contract could have multiple VRF requests in flight simultaneously,
* @dev you must ensure that the order in which the VRF responses arrive cannot
* @dev be used to manipulate your contract's user-significant behavior.
*
* @dev Since the block hash of the block which contains the requestRandomness
* @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
* @dev miner could, in principle, fork the blockchain to evict the block
* @dev containing the request, forcing the request to be included in a
* @dev different block with a different hash, and therefore a different input
* @dev to the VRF. However, such an attack would incur a substantial economic
* @dev cost. This cost scales with the number of blocks the VRF oracle waits
* @dev until it calls responds to a request. It is for this reason that
* @dev that you can signal to an oracle you'd like them to wait longer before
* @dev responding to the request (however this is not enforced in the contract
* @dev and so remains effective only in the case of unmodified oracle software).
*/
abstract contract VRFConsumerBaseV2Plus is IVRFMigratableConsumerV2Plus, ConfirmedOwner {
error OnlyCoordinatorCanFulfill(address have, address want);
error OnlyOwnerOrCoordinator(address have, address owner, address coordinator);
error ZeroAddress();
// s_vrfCoordinator should be used by consumers to make requests to vrfCoordinator
// so that coordinator reference is updated after migration
IVRFCoordinatorV2Plus public s_vrfCoordinator;
/**
* @param _vrfCoordinator address of VRFCoordinator contract
*/
constructor(address _vrfCoordinator) ConfirmedOwner(msg.sender) {
if (_vrfCoordinator == address(0)) {
revert ZeroAddress();
}
s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
}
/**
* @notice fulfillRandomness handles the VRF response. Your contract must
* @notice implement it. See "SECURITY CONSIDERATIONS" above for important
* @notice principles to keep in mind when implementing your fulfillRandomness
* @notice method.
*
* @dev VRFConsumerBaseV2Plus expects its subcontracts to have a method with this
* @dev signature, and will call it once it has verified the proof
* @dev associated with the randomness. (It is triggered via a call to
* @dev rawFulfillRandomness, below.)
*
* @param requestId The Id initially returned by requestRandomness
* @param randomWords the VRF output expanded to the requested number of words
*/
// solhint-disable-next-line chainlink-solidity/prefix-internal-functions-with-underscore
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal virtual;
// rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
// proof. rawFulfillRandomness then calls fulfillRandomness, after validating
// the origin of the call
function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external {
if (msg.sender != address(s_vrfCoordinator)) {
revert OnlyCoordinatorCanFulfill(msg.sender, address(s_vrfCoordinator));
}
fulfillRandomWords(requestId, randomWords);
}
/**
* @inheritdoc IVRFMigratableConsumerV2Plus
*/
function setCoordinator(address _vrfCoordinator) external override onlyOwnerOrCoordinator {
if (_vrfCoordinator == address(0)) {
revert ZeroAddress();
}
s_vrfCoordinator = IVRFCoordinatorV2Plus(_vrfCoordinator);
emit CoordinatorSet(_vrfCoordinator);
}
modifier onlyOwnerOrCoordinator() {
if (msg.sender != owner() && msg.sender != address(s_vrfCoordinator)) {
revert OnlyOwnerOrCoordinator(msg.sender, owner(), address(s_vrfCoordinator));
}
_;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC-721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC-721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)
pragma solidity ^0.8.20;
import {IERC721Receiver} from "../IERC721Receiver.sol";
/**
* @dev Implementation of the {IERC721Receiver} interface.
*
* Accepts all token transfers.
* Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
* {IERC721-setApprovalForAll}.
*/
abstract contract ERC721Holder is IERC721Receiver {
/**
* @dev See {IERC721Receiver-onERC721Received}.
*
* Always returns `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
return this.onERC721Received.selector;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 1);
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 1);
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be 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 ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
// ███╗ ███╗███████╗████████╗ █████╗ ██╗ ██╗██╗███╗ ██╗
// ████╗ ████║╚══════╝╚══██╔══╝██╔══██╗██║ ██║██║████╗ ██║
// ██╔████╔██║ █████╗ ██║ ███████║██║ █╗ ██║██║██╔██╗ ██║
// ██║╚██╔╝██║ ╚════╝ ██║ ██╔══██║██║███╗██║██║██║╚██╗██║
// ██║ ╚═╝ ██║███████╗ ██║ ██║ ██║╚███╔███╔╝██║██║ ╚████║
// ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝╚═╝ ╚═══╝
/// @notice Simple interface for checking if an account is blacklisted on the USDC contract.
interface IUSDC {
/**
* @notice Checks if account is blacklisted.
* @param _account The address to check.
* @return bool true if the account is blacklisted, false if the account is not blacklisted.
*/
function isBlacklisted(address _account) external view returns (bool);
}{
"optimizer": {
"enabled": true,
"runs": 10000
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_platformWallet","type":"address"},{"internalType":"address","name":"_usdc","type":"address"},{"internalType":"uint256","name":"_subscriptionId","type":"uint256"},{"internalType":"address","name":"_vrfCoordinator","type":"address"},{"internalType":"bytes32","name":"_keyHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"FeesAlreadyClaimed","type":"error"},{"inputs":[],"name":"GatingTokenBalanceTooLow","type":"error"},{"inputs":[],"name":"InvalidCommissionPercentage","type":"error"},{"inputs":[],"name":"InvalidFeeSupplied","type":"error"},{"inputs":[],"name":"InvalidFreeEntry","type":"error"},{"inputs":[],"name":"InvalidGatingToken","type":"error"},{"inputs":[],"name":"InvalidNativeTokenAmountStaked","type":"error"},{"inputs":[],"name":"InvalidPriceTier","type":"error"},{"inputs":[],"name":"InvalidPriceTiersCount","type":"error"},{"inputs":[],"name":"InvalidPrize","type":"error"},{"inputs":[],"name":"InvalidPrizesCount","type":"error"},{"inputs":[],"name":"InvalidStatus","type":"error"},{"inputs":[],"name":"NativeTokenTransferFailed","type":"error"},{"inputs":[],"name":"NotEnoughFeesRaised","type":"error"},{"inputs":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"want","type":"address"}],"name":"OnlyCoordinatorCanFulfill","type":"error"},{"inputs":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"coordinator","type":"address"}],"name":"OnlyOwnerOrCoordinator","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"RandomnessRequestAlreadyFulfilled","type":"error"},{"inputs":[],"name":"RandomnessRequestDoesNotExist","type":"error"},{"inputs":[],"name":"RandomnessRequestResetTooEarly","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vrfCoordinator","type":"address"}],"name":"CoordinatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint48","name":"entriesCount","type":"uint48"},{"indexed":false,"internalType":"uint256","name":"priceTierId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"brandId","type":"uint8"}],"name":"EntrySold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"platformFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ownerFees","type":"uint256"}],"name":"FeesTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"buyer","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint48","name":"entriesCount","type":"uint48"},{"indexed":false,"internalType":"uint8","name":"brandId","type":"uint8"}],"name":"FreeEntry","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"PauseToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"prizeId","type":"uint8"},{"indexed":false,"internalType":"address","name":"winner","type":"address"}],"name":"PrizeTransferFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"prizeId","type":"uint8"},{"indexed":false,"internalType":"address","name":"winner","type":"address"}],"name":"PrizeTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountRaised","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"RaffleCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountRaised","type":"uint256"},{"indexed":false,"internalType":"uint48","name":"entries","type":"uint48"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"RaffleClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"priceTierCount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"prizeCount","type":"uint256"}],"name":"RaffleCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"winners","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"RaffleDrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"RaffleStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"randomness","type":"uint256[]"}],"name":"RandomnessReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"RandomnessRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"},{"internalType":"uint256","name":"_priceTierIndex","type":"uint256"},{"internalType":"uint8","name":"_brandId","type":"uint8"}],"name":"buyEntry","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"cancelRaffle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"commissionInBasisPoints","type":"uint16"},{"internalType":"uint128","name":"minimumFees","type":"uint128"},{"components":[{"internalType":"uint48","name":"numEntries","type":"uint48"},{"internalType":"uint160","name":"price","type":"uint160"}],"internalType":"struct RaffleV3.PriceTierCallData[]","name":"priceTiers","type":"tuple[]"},{"components":[{"internalType":"enum RaffleV3.TokenType","name":"prizeType","type":"uint8"},{"internalType":"address","name":"prizeAddress","type":"address"},{"internalType":"uint256","name":"prizeNumber","type":"uint256"}],"internalType":"struct RaffleV3.PrizeCallData[]","name":"prizes","type":"tuple[]"}],"internalType":"struct RaffleV3.CreateRaffleCallData","name":"_params","type":"tuple"}],"name":"createRaffle","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"drawWinners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"freeEntryUsed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"getRaffle","outputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"enum RaffleV3.RaffleStatus","name":"status","type":"uint8"},{"internalType":"uint48","name":"entryCount","type":"uint48"},{"internalType":"uint16","name":"platformPercentage","type":"uint16"},{"internalType":"bool","name":"feesClaimed","type":"bool"},{"internalType":"bool","name":"isGated","type":"bool"},{"internalType":"uint128","name":"fees","type":"uint128"},{"internalType":"uint128","name":"minimumFees","type":"uint128"},{"components":[{"internalType":"uint48","name":"tierId","type":"uint48"},{"internalType":"uint48","name":"numEntries","type":"uint48"},{"internalType":"uint160","name":"price","type":"uint160"}],"internalType":"struct RaffleV3.PriceTier[]","name":"priceTiers","type":"tuple[]"},{"components":[{"internalType":"uint8","name":"prizeId","type":"uint8"},{"internalType":"bool","name":"failed","type":"bool"},{"internalType":"enum RaffleV3.TokenType","name":"prizeType","type":"uint8"},{"internalType":"address","name":"prizeAddress","type":"address"},{"internalType":"uint256","name":"prizeNumber","type":"uint256"}],"internalType":"struct RaffleV3.Prize[]","name":"prizes","type":"tuple[]"},{"components":[{"internalType":"uint48","name":"currentEntriesLength","type":"uint48"},{"internalType":"address","name":"player","type":"address"}],"internalType":"struct RaffleV3.Entry[]","name":"entries","type":"tuple[]"},{"components":[{"internalType":"uint8","name":"prizeId","type":"uint8"},{"internalType":"address","name":"player","type":"address"},{"internalType":"uint256","name":"randomNumber","type":"uint256"}],"internalType":"struct RaffleV3.Winner[]","name":"winners","type":"tuple[]"}],"internalType":"struct RaffleV3.Raffle","name":"raffle","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"getWinners","outputs":[{"components":[{"internalType":"uint8","name":"prizeId","type":"uint8"},{"internalType":"address","name":"player","type":"address"},{"internalType":"uint256","name":"randomNumber","type":"uint256"}],"internalType":"struct RaffleV3.Winner[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"},{"internalType":"address[]","name":"_addresses","type":"address[]"},{"internalType":"uint8","name":"_brandId","type":"uint8"}],"name":"giveBatchEntriesForFree","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformWallet","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"raffles","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"enum RaffleV3.RaffleStatus","name":"status","type":"uint8"},{"internalType":"uint48","name":"entryCount","type":"uint48"},{"internalType":"uint16","name":"platformPercentage","type":"uint16"},{"internalType":"bool","name":"feesClaimed","type":"bool"},{"internalType":"bool","name":"isGated","type":"bool"},{"internalType":"uint128","name":"fees","type":"uint128"},{"internalType":"uint128","name":"minimumFees","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rafflesCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"randomnessLookup","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"randomnessRequests","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"bool","name":"manual","type":"bool"},{"internalType":"bool","name":"fulfilled","type":"bool"},{"internalType":"uint48","name":"requestTime","type":"uint48"},{"internalType":"uint80","name":"raffleId","type":"uint80"},{"internalType":"uint256","name":"randomWord","type":"uint256"}],"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":[{"internalType":"uint256","name":"_raffleId","type":"uint256"},{"internalType":"uint8","name":"_prizeId","type":"uint8"}],"name":"recoverPrize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"removeTokenGate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"resetRandomnessRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"s_keyHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"s_subscriptionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"s_vrfCoordinator","outputs":[{"internalType":"contract IVRFCoordinatorV2Plus","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vrfCoordinator","type":"address"}],"name":"setCoordinator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_keyHash","type":"bytes32"}],"name":"setKeyHash","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_newAddress","type":"address"}],"name":"setPlatformAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"setSubscriptionId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"},{"internalType":"enum RaffleV3.TokenType","name":"_tokenType","type":"uint8"},{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint88","name":"_tokenAmount","type":"uint88"}],"name":"setTokenGate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"},{"internalType":"bool","name":"_manual","type":"bool"}],"name":"setWinners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"togglePaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenGates","outputs":[{"internalType":"enum RaffleV3.TokenType","name":"tokenType","type":"uint8"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint88","name":"tokenAmount","type":"uint88"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"transferFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_raffleId","type":"uint256"}],"name":"transferPrizes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usdc","outputs":[{"internalType":"contract IUSDC","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a06040526005805460ff60a01b19169055600880546001600160501b03191666010003002625a017905534801561003657600080fd5b50604051616271380380616271833981016040819052610055916102f8565b600180558133806000816100b05760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600280546001600160a01b0319166001600160a01b03848116919091179091558116156100e0576100e081610186565b5050506001600160a01b03811661010a5760405163d92e233d60e01b815260040160405180910390fd5b600480546001600160a01b03199081166001600160a01b039384161790915560058054909116878316179055841660805260068390556007819055610150600033610230565b5061017b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c33610230565b50505050505061034f565b336001600160a01b038216036101de5760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016100a7565b600380546001600160a01b0319166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b6000828152602081815260408083206001600160a01b038516845290915281205460ff166102d2576000838152602081815260408083206001600160a01b03861684529091529020805460ff1916600117905561028a3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016102d6565b5060005b92915050565b80516001600160a01b03811681146102f357600080fd5b919050565b600080600080600060a0868803121561031057600080fd5b610319866102dc565b9450610327602087016102dc565b93506040860151925061033c606087016102dc565b9150608086015190509295509295909350565b608051615ef261037f600039600081816104fb015281816145e10152818161465801526147010152615ef26000f3fe6080604052600436106102f25760003560e01c80638ac000211161018f578063b09e6621116100e1578063d547741f1161008a578063f5b541a611610064578063f5b541a614610ada578063f6f1306014610b0e578063fa2af9da14610b2e57600080fd5b8063d547741f14610a6d578063e4dafec914610a8d578063f2fde38b14610aba57600080fd5b8063c1e0d236116100bb578063c1e0d23614610a0d578063cc03c34214610a2d578063d1d06b2e14610a4d57600080fd5b8063b09e662114610957578063b5eb666614610977578063bf19d77a146109ed57600080fd5b80639854471011610143578063a694fc3a1161011d578063a694fc3a146108e7578063a942f56a146108fa578063aa7029da1461092a57600080fd5b806398544710146108925780639eccacf6146108b2578063a217fddf146108d257600080fd5b80638ea98117116101745780638ea981171461080e578063903978a51461082e57806391d148541461084e57600080fd5b80638ac00021146107da5780638da5cb5b146107f057600080fd5b80633c0da92a116102485780635c975abb116101fc5780635fba3171116101d65780635fba3171146107785780636b1426a41461079857806379ba5097146107c557600080fd5b80635c975abb146105815780635cb6dfff146105a25780635d4bc0ce1461066057600080fd5b8063409f91031161022d578063409f91031461053557806345bb327b146105555780634d2c53cd1461056b57600080fd5b80633c0da92a146104c95780633e413bee146104e957600080fd5b806328e7a854116102aa57806334d6b3661161028457806334d6b3661461048157806336566f061461049457806336568abe146104a957600080fd5b806328e7a854146104215780632a1234fb146104415780632f2ff15d1461046157600080fd5b80631c556c17116102db5780631c556c17146103a15780631fe543e3146103c3578063248a9ca3146103e357600080fd5b806301ffc9a7146102f7578063150b7a021461032c575b600080fd5b34801561030357600080fd5b5061031761031236600461507c565b610b4e565b60405190151581526020015b60405180910390f35b34801561033857600080fd5b50610370610347366004615133565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610323565b3480156103ad57600080fd5b506103c16103bc366004615212565b610be7565b005b3480156103cf57600080fd5b506103c16103de36600461523e565b610d49565b3480156103ef57600080fd5b506104136103fe3660046152bf565b60009081526020819052604090206001015490565b604051908152602001610323565b34801561042d57600080fd5b506103c161043c3660046152bf565b610db3565b34801561044d57600080fd5b506103c161045c3660046152bf565b610f8b565b34801561046d57600080fd5b506103c161047c3660046152d8565b610ff7565b6103c161048f366004615308565b611022565b3480156104a057600080fd5b506103c161152b565b3480156104b557600080fd5b506103c16104c43660046152d8565b6115d7565b3480156104d557600080fd5b506104136104e436600461533d565b611623565b3480156104f557600080fd5b5061051d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610323565b34801561054157600080fd5b506103c1610550366004615386565b611cd2565b34801561056157600080fd5b5061041360075481565b34801561057757600080fd5b50610413600b5481565b34801561058d57600080fd5b5060055461031790600160a01b900460ff1681565b3480156105ae57600080fd5b506106186105bd3660046152bf565b6009602052600090815260409020805460019091015460ff808316926101008104821692620100008204909216916301000000820465ffffffffffff16916901000000000000000000900469ffffffffffffffffffff169086565b60408051961515875294151560208701529215159385019390935265ffffffffffff16606084015269ffffffffffffffffffff909116608083015260a082015260c001610323565b34801561066c57600080fd5b5061076461067b3660046152bf565b600c60205260009081526040902080546001909101546001600160a01b0382169160ff600160a01b820481169265ffffffffffff75010000000000000000000000000000000000000000008404169261ffff7b01000000000000000000000000000000000000000000000000000000820416927d01000000000000000000000000000000000000000000000000000000000082048116927e0100000000000000000000000000000000000000000000000000000000000090920416906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041688565b6040516103239897969594939291906153ee565b34801561078457600080fd5b506103c16107933660046152bf565b611e26565b3480156107a457600080fd5b506107b86107b33660046152bf565b6121d2565b604051610323919061545e565b3480156107d157600080fd5b506103c161226d565b3480156107e657600080fd5b5061041360065481565b3480156107fc57600080fd5b506002546001600160a01b031661051d565b34801561081a57600080fd5b506103c16108293660046154c6565b612354565b34801561083a57600080fd5b506103c16108493660046154e3565b61248b565b34801561085a57600080fd5b506103176108693660046152d8565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561089e57600080fd5b506103c16108ad3660046152bf565b6127cd565b3480156108be57600080fd5b5060045461051d906001600160a01b031681565b3480156108de57600080fd5b50610413600081565b6103c16108f53660046152bf565b6127de565b34801561090657600080fd5b506103176109153660046152bf565b600d6020526000908152604090205460ff1681565b34801561093657600080fd5b506104136109453660046152bf565b600a6020526000908152604090205481565b34801561096357600080fd5b506103c16109723660046155be565b612bbe565b34801561098357600080fd5b506109de6109923660046152bf565b600e6020526000908152604090205460ff81169061010081046001600160a01b031690750100000000000000000000000000000000000000000090046affffffffffffffffffffff1683565b6040516103239392919061562f565b3480156109f957600080fd5b506103c1610a083660046152bf565b612d2e565b348015610a1957600080fd5b506103c1610a283660046152bf565b612e1b565b348015610a3957600080fd5b506103c1610a483660046154c6565b612e2c565b348015610a5957600080fd5b506103c1610a683660046152bf565b612e72565b348015610a7957600080fd5b506103c1610a883660046152d8565b612f55565b348015610a9957600080fd5b50610aad610aa83660046152bf565b612f7a565b60405161032391906157f4565b348015610ac657600080fd5b506103c1610ad53660046154c6565b61338d565b348015610ae657600080fd5b506104137f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c81565b348015610b1a57600080fd5b506103c1610b293660046152bf565b61339e565b348015610b3a57600080fd5b5060055461051d906001600160a01b031681565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610be157507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c610c1181613481565b600554600160a01b900460ff1615610c55576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c5d61348b565b6000838152600c6020526040812060038101805491929160ff8616908110610c8757610c87615925565b60009182526020918290206040805160a0810182526002938402909201805460ff80821685526101008204811615159685019690965292949093918501926201000090041690811115610cdc57610cdc6153ab565b6002811115610ced57610ced6153ab565b81528154630100000090046001600160a01b031660208083019190915260019092015460409091015281015190915015610d39578154610d3990869083906001600160a01b03166134ce565b5050610d4460018055565b505050565b6004546001600160a01b03163314610da857600480546040517f1cf993f400000000000000000000000000000000000000000000000000000000815233928101929092526001600160a01b031660248201526044015b60405180910390fd5b610d44838383613835565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c610ddd81613481565b610de561348b565b6000828152600c60209081526040808320600a83528184205484526009909252909120805460ff16610e43576040517f1b71a84c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805462010000900460ff1615610e85576040517f3975fee200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038254600160a01b900460ff166008811115610ea457610ea46153ab565b14610edb576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80544290610efa906301000000900465ffffffffffff1661012c615983565b65ffffffffffff161115610f3a576040517fad55303b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805460ff1916905580547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674020000000000000000000000000000000000000000179055505060018055565b5050565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c610fb581613481565b506000908152600c6020908152604080832080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055600e909152812055565b60008281526020819052604090206001015461101281613481565b61101c83836139a9565b50505050565b600554600160a01b900460ff1615611066576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61106e61348b565b6000838152600c6020526040902060028154600160a01b900460ff16600881111561109b5761109b6153ab565b146110d2576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547e01000000000000000000000000000000000000000000000000000000000000900460ff1615611108576111088433613a53565b6002810154808410611146576040517f1dbcf8aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082600201858154811061115d5761115d615925565b600091825260209182902060408051606081018252929091015465ffffffffffff80821684526601000000000000820416938301939093526c010000000000000000000000009092046001600160a01b0316918101829052915034146111ef576040517fad80e5f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80604001516001600160a01b0316600003611296576040805133602082015290810187905260009060600160408051601f1981840301815291815281516020928301206000818152600d90935291205490915060ff161561127c576040517fd72456a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000908152600d60205260409020805460ff191660011790555b82547501000000000000000000000000000000000000000000900465ffffffffffff166000036113685733836004016000815481106112d7576112d7615925565b9060005260206000200160000160066101000a8154816001600160a01b0302191690836001600160a01b0316021790555080602001518360040160008154811061132357611323615925565b600091825260209091200180547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff9290921691909117905561140f565b6000604051806040016040528083602001518660000160159054906101000a900465ffffffffffff1661139b9190615983565b65ffffffffffff90811682523360209283015260048701805460018101825560009182529083902084519101805494909301516001600160a01b03166601000000000000027fffffffffffff0000000000000000000000000000000000000000000000000000909416911617919091179055505b6001830180543491906000906114389084906fffffffffffffffffffffffffffffffff166159a1565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555080602001518360000160158282829054906101000a900465ffffffffffff166114979190615983565b82546101009290920a65ffffffffffff818102199093169183160217909155845460408051750100000000000000000000000000000000000000000090920490921681526020810188905260ff87169181019190915233915087907f0741232ab3faf4ebed1295982370d6148eb0ecbc8e76cf8a3492a337b84cff239060600160405180910390a3505050610d4460018055565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c61155581613481565b6005805460ff600160a01b80830482161581027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90931692909217928390556040517f9077d36bc00859b5c3f320310707208543dd35092cb0a0fe117d0c6a558b148b936115cc9390049091161515815260200190565b60405180910390a150565b6001600160a01b0381163314611619576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d448282613c44565b60007f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c61164f81613481565b61165761348b565b600061166660408501856159c9565b9150600090506116796060860186615a38565b90509050816000036116b7576040517f87a097f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8015806116c45750600a81115b156116fb576040517fab13062d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61271061170b6020870187615a9f565b61ffff161115611747576040517fb9d3a94500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600b6000815461175890615ac3565b91829055506000818152600c6020908152604090912080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b1781559192506117a990880188615a9f565b815461ffff919091167b01000000000000000000000000000000000000000000000000000000027fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161781556118076040880160208901615afb565b6001820180546fffffffffffffffffffffffffffffffff92831670010000000000000000000000000000000002921691909117905560005b838110156119ed57600061185660608a018a615a38565b8381811061186657611866615925565b90506060020180360381019061187c9190615b2d565b9050600281516002811115611893576118936153ab565b11806118bf575060408101511580156118bf57506002815160028111156118bc576118bc6153ab565b14155b806118f357506000815160028111156118da576118da6153ab565b141580156118f3575060208101516001600160a01b0316155b1561192a576040517f5f12e2ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038301805460018101825560009182526020909120600291820201805460ff851660ff198216811783558451929384927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff00169091179062010000908490811115611997576119976153ab565b0217905550602082015181546001600160a01b039091166301000000027fffffffffffffffffff0000000000000000000000000000000000000000ffffff9091161781556040909101516001918201550161183f565b5060005b84811015611bc1576000611a0860408a018a6159c9565b83818110611a1857611a18615925565b905060400201803603810190611a2e9190615b98565b805190915065ffffffffffff161580611a5c575060208101516001600160a01b0316158015611a5c57508115155b15611a93576040517f1dbcf8aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082118015611aee575060028301611aad600184615c04565b81548110611abd57611abd615925565b60009182526020918290200154908201516001600160a01b036c010000000000000000000000009092048216911611155b15611b25576040517f1dbcf8aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002830180546001818101835560009283526020928390209091018054845194909301516001600160a01b03166c01000000000000000000000000026bffffffffffffffffffffffff65ffffffffffff9586166601000000000000027fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090951695871695909517939093179390931691909117909155016119f1565b50604080518082019091526000808252602080830182815260048501805460018101825581855292842085519301805492516001600160a01b03166601000000000000027fffffffffffff000000000000000000000000000000000000000000000000000090931665ffffffffffff9490941693909317919091179091558054909190611c5057611c50615925565b60009182526020918290200180547fffffffffffff00000000000000000000000000000000000000000000000000001690556040805187815291820186905284917f9aa2065fb625f0039ef34f9a9cb1fe8e45055c5aa4d830c016505ee55a598a84910160405180910390a250909450505050611ccc60018055565b50919050565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c611cfc81613481565b600554600160a01b900460ff1615611d40576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d4861348b565b6000838152600c6020526040902060028154600160a01b900460ff166008811115611d7557611d756153ab565b14611dac576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018101546fffffffffffffffffffffffffffffffff7001000000000000000000000000000000008204811691161015611e12576040517f2cc7ed9d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e1c8484613cc7565b50610d4460018055565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c611e5081613481565b611e5861348b565b6000828152600c6020526040902060058154600160a01b900460ff166008811115611e8557611e856153ab565b1480611ead575060068154600160a01b900460ff166008811115611eab57611eab6153ab565b145b80611ed4575060078154600160a01b900460ff166008811115611ed257611ed26153ab565b145b80611efb575060088154600160a01b900460ff166008811115611ef957611ef96153ab565b145b15611f32576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028154600160a01b900460ff166008811115611f5157611f516153ab565b1480611f79575060038154600160a01b900460ff166008811115611f7757611f776153ab565b145b156121395760005b600382015481101561204d576000826003018281548110611fa457611fa4615925565b60009182526020918290206040805160a0810182526002938402909201805460ff80821685526101008204811615159685019690965292949093918501926201000090041690811115611ff957611ff96153ab565b600281111561200a5761200a6153ab565b815281546001600160a01b0363010000009091048116602083015260019092015460409091015284549192506120449187918491166134ce565b50600101611f81565b5060055460018201546040516000926001600160a01b0316916fffffffffffffffffffffffffffffffff16908381818185875af1925050503d80600081146120b1576040519150601f19603f3d011682016040523d82523d6000602084013e6120b6565b606091505b50509050806120f1576040517f3022f2e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5080547fffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167d0100000000000000000000000000000000000000000000000000000000001781555b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740800000000000000000000000000000000000000001781556001810154604080516fffffffffffffffffffffffffffffffff9092168252426020830152805185927f50d7e951bb520133496ca2822d7c0578a201ff4bbbb79ed9a5e773fdc24ca10b92908290030190a250610f8760018055565b6060600c6000838152602001908152602001600020600501805480602002602001604051908101604052809291908181526020016000905b828210156122625760008481526020908190206040805160608101825260028602909201805460ff8116845261010090046001600160a01b03168385015260019081015491830191909152908352909201910161220a565b505050509050919050565b6003546001600160a01b031633146122e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610d9f565b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000808216339081179093556003805490911690556040516001600160a01b03909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6002546001600160a01b0316331480159061237a57506004546001600160a01b03163314155b156123e557336123926002546001600160a01b031690565b600480546040517f061db9c10000000000000000000000000000000000000000000000000000000081526001600160a01b0394851692810192909252918316602482015291166044820152606401610d9f565b6001600160a01b038116612425576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fd1a6a14209a385a964d036e404cb5cfb71f4000cdb03c9366292430787261be6906020016115cc565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c6124b581613481565b600554600160a01b900460ff16156124f9576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61250161348b565b6000848152600c6020526040902060028154600160a01b900460ff16600881111561252e5761252e6153ab565b14612565576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83516000805b8281101561271557600087828151811061258757612587615925565b6020026020010151905060006040518060400160405280848860000160159054906101000a900465ffffffffffff1665ffffffffffff166125c89190615c17565b6125d3906001615c17565b65ffffffffffff90811682526001600160a01b038516602090920191909152875491925075010000000000000000000000000000000000000000009091041615801561261d575082155b1561269b57808660040160008154811061263957612639615925565b600091825260209182902083519101805493909201516001600160a01b03166601000000000000027fffffffffffff000000000000000000000000000000000000000000000000000090931665ffffffffffff90911617919091179055612707565b6004860180546001810182556000918252602091829020835191018054928401516001600160a01b03166601000000000000027fffffffffffff000000000000000000000000000000000000000000000000000090931665ffffffffffff909216919091179190911790555b50506001918201910161256b565b5082548190849060159061274b9084907501000000000000000000000000000000000000000000900465ffffffffffff16615983565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550867f43b4167774874553cc57c1c23bbc4589ed22b35aba69867e65720bbabbd3c23c87848660000160159054906101000a900465ffffffffffff16896040516127b99493929190615c65565b60405180910390a250505061101c60018055565b60006127d881613481565b50600755565b6127e661348b565b6000818152600c60205260409020600381015460018254600160a01b900460ff166008811115612818576128186153ab565b1461284f576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81547fffffffffffffffffffffffff000000000000000000000000000000000000000016331782556000805b82811015612b0157600084600301828154811061289a5761289a615925565b60009182526020918290206040805160a0810182526002938402909201805460ff808216855261010082048116151596850196909652929490939185019262010000900416908111156128ef576128ef6153ab565b6002811115612900576129006153ab565b81528154630100000090046001600160a01b03166020820152600190910154604090910152905060008160400151600281111561293f5761293f6153ab565b0361295a5760808101516129539084615c17565b9250612af8565b600181604001516002811115612972576129726153ab565b03612a1957606081015160808201516040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101919091526001600160a01b03909116906323b872dd906064016020604051808303816000875af11580156129ef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a139190615c9f565b50612af8565b600281604001516002811115612a3157612a316153ab565b03612ac657606081015160808201516040517f42842e0e00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101919091526001600160a01b03909116906342842e0e90606401600060405180830381600087803b158015612aa957600080fd5b505af1158015612abd573d6000803e3d6000fd5b50505050612af8565b6040517f5f12e2ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060010161287b565b5082547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674020000000000000000000000000000000000000000178355348114612b78576040517fd8bd2c2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051428152339085907f8520a64b531077689214cabcfa489eef68e4b29ffcb6f1e22cd8fe4cffe706de9060200160405180910390a3505050612bbb60018055565b50565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c612be881613481565b6000858152600c6020526040902080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001781556001600160a01b0384161580612c5e57506affffffffffffffffffffff8316155b15612c95576040517f8826122a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000868152600e6020526040902080548690829060ff19166001836002811115612cc157612cc16153ab565b021790555080546affffffffffffffffffffff90941675010000000000000000000000000000000000000000000274ffffffffffffffffffffffffffffffffffffffffff6001600160a01b03909616610100029590951660ff909416939093179390931790915550505050565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c612d5881613481565b600554600160a01b900460ff1615612d9c576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612da461348b565b6000828152600c6020526040902060068154600160a01b900460ff166008811115612dd157612dd16153ab565b14612e08576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612e1183613ee4565b50610f8760018055565b6000612e2681613481565b50600655565b6000612e3781613481565b50600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c612e9c81613481565b600554600160a01b900460ff1615612ee0576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612ee861348b565b6000828152600c6020526040902060048154600160a01b900460ff166008811115612f1557612f156153ab565b14612f4c576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612e1183614153565b600082815260208190526040902060010154612f7081613481565b61101c8383613c44565b6040805161018080820183526000808352602080840182905283850182905260608085018390526080850183905260a0850183905260c0850183905260e08501839052610100850181905261012085018190526101408501819052610160850152858252600c815290849020845192830190945283546001600160a01b03811683529293919290830190600160a01b900460ff16600881111561301f5761301f6153ab565b6008811115613030576130306153ab565b8152815465ffffffffffff750100000000000000000000000000000000000000000082041660208084019190915261ffff7b0100000000000000000000000000000000000000000000000000000083041660408085019190915260ff7d01000000000000000000000000000000000000000000000000000000000084048116151560608601527e010000000000000000000000000000000000000000000000000000000000009093049092161515608084015260018401546fffffffffffffffffffffffffffffffff80821660a08601527001000000000000000000000000000000009091041660c084015260028401805483518184028101840190945280845260e090940193909160009084015b828210156131a6576000848152602090819020604080516060810182529185015465ffffffffffff80821684526601000000000000820416838501526c0100000000000000000000000090046001600160a01b03169082015282526001909201910161313f565b50505050815260200160038201805480602002602001604051908101604052809291908181526020016000905b8282101561327c5760008481526020908190206040805160a0810182526002808702909301805460ff8082168452610100820481161515968401969096529194909392850192620100009092041690811115613231576132316153ab565b6002811115613242576132426153ab565b81528154630100000090046001600160a01b03166020808301919091526001928301546040909201919091529183529290920191016131d3565b50505050815260200160048201805480602002602001604051908101604052809291908181526020016000905b828210156132fa576000848152602090819020604080518082019091529084015465ffffffffffff81168252660100000000000090046001600160a01b0316818301528252600190920191016132a9565b50505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b8282101561337f5760008481526020908190206040805160608101825260028602909201805460ff8116845261010090046001600160a01b031683850152600190810154918301919091529083529092019101613327565b505050915250909392505050565b6133956149bb565b612bbb81614a31565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c6133c881613481565b600554600160a01b900460ff161561340c576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61341461348b565b6000828152600c6020526040902060058154600160a01b900460ff166008811115613441576134416153ab565b14613478576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612e1183614b0d565b612bbb8133614d9a565b6002600154036134c7576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600155565b600080836040015160028111156134e7576134e76153ab565b1480156134f8575060008360800151115b156135cf5760808301516040516001600160a01b0384169190600081818185875af1925050503d806000811461354a576040519150601f19603f3d011682016040523d82523d6000602084013e61354f565b606091505b505080915050806135ca576000848152600c60205260409020835160038201805460019260ff1690811061358557613585615925565b600091825260209091206002909102018054911515610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff909216919091179055505b613789565b6001836040015160028111156135e7576135e76153ab565b036136b457606083015160808401516040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152602482019290925291169063a9059cbb906044016020604051808303816000875af192505050801561367b575060408051601f3d908101601f1916820190925261367891810190615c9f565b60015b6136aa576000848152600c60205260409020835160038201805460019260ff1690811061358557613585615925565b5060019050613789565b6002836040015160028111156136cc576136cc6153ab565b0361378957606083015160808401516040517f42842e0e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03858116602483015260448201929092529116906342842e0e90606401600060405180830381600087803b15801561374557600080fd5b505af1925050508015613756575060015b613785576000848152600c60205260409020835160038201805460019260ff1690811061358557613585615925565b5060015b80156137e05782516040805186815260ff90921660208301526001600160a01b03841682820152517f093c0e8b72bba93a9b348d23f07bdb7a9580015810b3e267daa431adf75a4ae99181900360600190a161101c565b82516040805186815260ff90921660208301526001600160a01b038416908201527f708a752d5a96615e830b607b8d5ffee8c131d33afa7699a5b571b952a93431b1906060015b60405180910390a150505050565b6000838152600960205260409020805460ff161561101c5780546901000000000000000000900469ffffffffffffffffffff166000818152600c60205260409081902090517f777b0ab3fa10335e90b469ef6a82310e1192541fc44fb3acaf2cc84a3dee474d906138ad908490899089908990615cbc565b60405180910390a160038154600160a01b900460ff1660088111156138d4576138d46153ab565b036139a15782547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff1662010000178355848460008161391557613915615925565b602002919091013560018501555080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740400000000000000000000000000000000000000001781558254610100900460ff161580156139815750600554600160a01b900460ff16155b156139a15761398f82614153565b61399882614b0d565b6139a182613ee4565b505050505050565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16613a4b576000838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055613a033390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610be1565b506000610be1565b6000828152600e60205260408082208151606081019092528054829060ff166002811115613a8357613a836153ab565b6002811115613a9457613a946153ab565b8152905461010081046001600160a01b03166020830152750100000000000000000000000000000000000000000090046affffffffffffffffffffff166040909101529050600181516002811115613aee57613aee6153ab565b03613bc957602081015160408083015190517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301526affffffffffffffffffffff909216918316906370a08231906024015b602060405180830381865afa158015613b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b919190615d1c565b101561101c576040517fccb3bd8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281516002811115613bde57613bde6153ab565b03610d4457602081015160408083015190517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301526affffffffffffffffffffff909216918316906370a0823190602401613b50565b6000828152602081815260408083206001600160a01b038516845290915281205460ff1615613a4b576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610be1565b6000828152600c6020908152604080832080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674030000000000000000000000000000000000000000178155600454825160c08101845260075481526006548186015260085461ffff6401000000008204168286015263ffffffff808216606084015266010000000000009091041660808201528351948501909352600184529093926001600160a01b0390911691639b1c385e9160a0820190613d8c90614e06565b8152506040518263ffffffff1660e01b8152600401613dab9190615d35565b6020604051808303816000875af1158015613dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dee9190615d1c565b6000818152600960209081526040808320805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009091166101008a15150217177fffffffffffffffffffffffffff00000000000000000000000000000000ffffff1663010000004265ffffffffffff16027fffffffffffffffffffffffffff00000000000000000000ffffffffffffffffff1617690100000000000000000069ffffffffffffffffffff8b1602179055878352600a82529182902083905581518781529081018390529192507f3d94fecedaa4f90b8bd459797adb95f5bb11426025c5541390d9ccc1ad1b60a19101613827565b6000818152600c6020526040902080547d010000000000000000000000000000000000000000000000000000000000900460ff1615613f4f576040517f45a1ea2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054600182015460009161271091613f9c917b01000000000000000000000000000000000000000000000000000000900461ffff16906fffffffffffffffffffffffffffffffff16615dd1565b613fa69190615e32565b60018301546fffffffffffffffffffffffffffffffff9182169250600091613fd091849116615c04565b6005546040519192506000916001600160a01b039091169084908381818185875af1925050503d8060008114614022576040519150601f19603f3d011682016040523d82523d6000602084013e614027565b606091505b505084546040519192506000916001600160a01b039091169084908381818185875af1925050503d806000811461407a576040519150601f19603f3d011682016040523d82523d6000602084013e61407f565b606091505b5050905081158061408e575080155b156140c5576040517f3022f2e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffff00ffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167d010000000000000000070000000000000000000000000000000000000000178555604080518781526020810186905280820185905290517ff22c03b9832b79146cb366b733d5f9b0a1cf3feb2c8bba254fb91e33f278358f9181900360600190a1505050505050565b6000818152600a60209081526040808320548084526009835292819020815160c081018352815460ff808216151580845261010083048216151596840196909652620100008204161515938201939093526301000000830465ffffffffffff166060820152690100000000000000000090920469ffffffffffffffffffff1660808301526001015460a082015290614217576040517f1b71a84c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600c6020526040902060048154600160a01b900460ff166008811115614244576142446153ab565b1461427b576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381015460048201805490600090614295600184615c04565b815481106142a5576142a5615925565b600091825260208220015465ffffffffffff811692506142d09060081c64ffffffffff166001615c17565b67ffffffffffffffff8111156142e8576142e86150d3565b604051908082528060200260200182016040528015614311578160200160208202803683370190505b5060a08701519091506000808667ffffffffffffffff811115614336576143366150d3565b60405190808252806020026020018201604052801561435f578160200160208202803683370190505b5090506000805b888110156149335760008a600301828154811061438557614385615925565b60009182526020918290206040805160a0810182526002938402909201805460ff808216855261010082048116151596850196909652929490939185019262010000900416908111156143da576143da6153ab565b60028111156143eb576143eb6153ab565b81528154630100000090046001600160a01b03166020820152600190910154604090910152905087831061455e578a5484516001600160a01b039091169085908490811061443b5761443b615925565b60200260200101906001600160a01b031690816001600160a01b0316815250508a6005016040518060600160405280836000015160ff1681526020018d60000160009054906101000a90046001600160a01b03166001600160a01b0316815260200188815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816001015550508160010191508560405160200161453d91815260200190565b6040516020818303038152906040528051906020012060001c955050614366565b614569868989614ea4565b985090965094508261457a81615ac3565b93506000905060048c0161458e8188614f74565b8154811061459e5761459e615925565b600091825260209091200154660100000000000090046001600160a01b031690506001826040015160028111156145d7576145d76153ab565b14801561461957507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682606001516001600160a01b0316145b80156146c357506040517ffe575a870000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063fe575a8790602401602060405180830381865afa15801561469f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146c39190615c9f565b15614813575b6040517ffe575a870000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063fe575a8790602401602060405180830381865afa158015614748573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061476c9190615c9f565b156148135788841061478957508a546001600160a01b0316614813565b6040805160208101899052016040516020818303038152906040528051906020012060001c96506147bb878a8a614ea4565b99509097509550836147cc81615ac3565b94505060048c016147dd8188614f74565b815481106147ed576147ed615925565b600091825260209091200154660100000000000090046001600160a01b031690506146c9565b8085848151811061482657614826615925565b60200260200101906001600160a01b031690816001600160a01b0316815250508b6005016040518060600160405280846000015160ff168152602001836001600160a01b0316815260200189815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816001015550508660405160200161490b91815260200190565b6040516020818303038152906040528051906020012060001c96508260010192505050614366565b5088547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740500000000000000000000000000000000000000001789556040518c907f61f33e70c53ae655df89109e261f76ef392f32df37ef1525cb02378613f93de1906149a59085904290615e72565b60405180910390a2505050505050505050505050565b6002546001600160a01b03163314614a2f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610d9f565b565b336001600160a01b03821603614aa3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610d9f565b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b6000818152600c6020526040902060058154600160a01b900460ff166008811115614b3a57614b3a6153ab565b14614b71576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b6005820154811015614d59576000826005018281548110614b9757614b97615925565b6000918252602091829020604080516060810182526002909302909101805460ff811684526001600160a01b036101009091041693830184905260010154908201529150614c9957614c948484600301836000015160ff1681548110614bff57614bff615925565b60009182526020918290206040805160a0810182526002938402909201805460ff80821685526101008204811615159685019690965292949093918501926201000090041690811115614c5457614c546153ab565b6002811115614c6557614c656153ab565b815281546001600160a01b036301000000909104811660208301526001909201546040909101528654166134ce565b614d50565b614d508484600301836000015160ff1681548110614cb957614cb9615925565b60009182526020918290206040805160a0810182526002938402909201805460ff80821685526101008204811615159685019690965292949093918501926201000090041690811115614d0e57614d0e6153ab565b6002811115614d1f57614d1f6153ab565b81528154630100000090046001600160a01b03166020808301919091526001909201546040909101528401516134ce565b50600101614b74565b5080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167406000000000000000000000000000000000000000017905550565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610f87576040517fe2517d3f0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260248101839052604401610d9f565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa82604051602401614e3f91511515815260200190565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915292915050565b600080606081614eb48688615e94565b614ebf906001615c17565b9050600881901c600160ff83161b5b80878381518110614ee157614ee1615925565b602002602001015116600014614f415760408051602081018b90520160408051601f1981840301815291905280516020909101209850614f21888a615e94565b614f2c906001615c17565b92505050600881901c600160ff83161b614ece565b80878381518110614f5457614f54615925565b602090810291909101018051919091179052509697909650939450505050565b81546000908103614f8757506000610be1565b82546000905b80821015614fe5576000614fa1838361505a565b905084868281548110614fb657614fb6615925565b60009182526020909120015465ffffffffffff161115614fd857809150614fdf565b8060010192505b50614f8d565b60008211801561502357508385614ffd600185615c04565b8154811061500d5761500d615925565b60009182526020909120015465ffffffffffff16145b1561505257507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019050610be1565b509050610be1565b60006150696002848418615ea8565b61507590848416615c17565b9392505050565b60006020828403121561508e57600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461507557600080fd5b6001600160a01b0381168114612bbb57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561512b5761512b6150d3565b604052919050565b6000806000806080858703121561514957600080fd5b8435615154816150be565b93506020850135615164816150be565b925060408501359150606085013567ffffffffffffffff81111561518757600080fd5b8501601f8101871361519857600080fd5b803567ffffffffffffffff8111156151b2576151b26150d3565b6151c56020601f19601f84011601615102565b8181528860208385010111156151da57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b803560ff8116811461520d57600080fd5b919050565b6000806040838503121561522557600080fd5b82359150615235602084016151fc565b90509250929050565b60008060006040848603121561525357600080fd5b83359250602084013567ffffffffffffffff81111561527157600080fd5b8401601f8101861361528257600080fd5b803567ffffffffffffffff81111561529957600080fd5b8660208260051b84010111156152ae57600080fd5b939660209190910195509293505050565b6000602082840312156152d157600080fd5b5035919050565b600080604083850312156152eb57600080fd5b8235915060208301356152fd816150be565b809150509250929050565b60008060006060848603121561531d57600080fd5b8335925060208401359150615334604085016151fc565b90509250925092565b60006020828403121561534f57600080fd5b813567ffffffffffffffff81111561536657600080fd5b82016080818503121561507557600080fd5b8015158114612bbb57600080fd5b6000806040838503121561539957600080fd5b8235915060208301356152fd81615378565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600981106153ea576153ea6153ab565b9052565b6001600160a01b0389168152610100810161540c602083018a6153da565b65ffffffffffff97909716604082015261ffff959095166060860152921515608085015290151560a08401526fffffffffffffffffffffffffffffffff90811660c08401521660e09091015292915050565b602080825282518282018190526000918401906040840190835b818110156154bb578351805160ff1684526020808201516001600160a01b0316908501526040908101519084015260608301602094909401939250600101615478565b509095945050505050565b6000602082840312156154d857600080fd5b8135615075816150be565b6000806000606084860312156154f857600080fd5b83359250602084013567ffffffffffffffff81111561551657600080fd5b8401601f8101861361552757600080fd5b803567ffffffffffffffff811115615541576155416150d3565b8060051b61555160208201615102565b9182526020818401810192908101908984111561556d57600080fd5b6020850194505b8385101561559b5784359250615589836150be565b82825260209485019490910190615574565b8096505050505050615334604085016151fc565b80356003811061520d57600080fd5b600080600080608085870312156155d457600080fd5b843593506155e4602086016155af565b925060408501356155f4816150be565b915060608501356affffffffffffffffffffff8116811461561457600080fd5b939692955090935050565b600381106153ea576153ea6153ab565b6060810161563d828661561f565b6001600160a01b03841660208301526affffffffffffffffffffff83166040830152949350505050565b600081518084526020840193506020830160005b828110156156ce57815165ffffffffffff815116875265ffffffffffff60208201511660208801526001600160a01b0360408201511660408801525060608601955060208201915060018101905061567b565b5093949350505050565b600081518084526020840193506020830160005b828110156156ce57815160ff8151168752602081015115156020880152604081015161571b604089018261561f565b506060818101516001600160a01b0316908801526080908101519087015260a090950194602091909101906001016156ec565b600081518084526020840193506020830160005b828110156156ce578151805165ffffffffffff1687526020908101516001600160a01b03168188015260409096019590910190600101615762565b600081518084526020840193506020830160005b828110156156ce578151805160ff1687526020808201516001600160a01b03169088015260409081015190870152606086019550602091909101906001016157b1565b6020815261580e6020820183516001600160a01b03169052565b6000602083015161582260408401826153da565b50604083015165ffffffffffff8116606084015250606083015161ffff8116608084015250608083015180151560a08401525060a083015180151560c08401525060c08301516fffffffffffffffffffffffffffffffff811660e08401525060e08301516fffffffffffffffffffffffffffffffff8116610100840152506101008301516101806101208401526158bd6101a0840182615667565b9050610120840151601f19848303016101408501526158dc82826156d8565b915050610140840151601f19848303016101608501526158fc828261574e565b915050610160840151601f198483030161018085015261591c828261579d565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b65ffffffffffff8181168382160190811115610be157610be1615954565b6fffffffffffffffffffffffffffffffff8181168382160190811115610be157610be1615954565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126159fe57600080fd5b83018035915067ffffffffffffffff821115615a1957600080fd5b6020019150600681901b3603821315615a3157600080fd5b9250929050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615a6d57600080fd5b83018035915067ffffffffffffffff821115615a8857600080fd5b6020019150606081023603821315615a3157600080fd5b600060208284031215615ab157600080fd5b813561ffff8116811461507557600080fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615af457615af4615954565b5060010190565b600060208284031215615b0d57600080fd5b81356fffffffffffffffffffffffffffffffff8116811461507557600080fd5b60006060828403128015615b4057600080fd5b506040516060810167ffffffffffffffff81118282101715615b6457615b646150d3565b604052615b70836155af565b81526020830135615b80816150be565b60208201526040928301359281019290925250919050565b60006040828403128015615bab57600080fd5b506040805190810167ffffffffffffffff81118282101715615bcf57615bcf6150d3565b604052823565ffffffffffff81168114615be857600080fd5b81526020830135615bf8816150be565b60208201529392505050565b81810381811115610be157610be1615954565b80820180821115610be157610be1615954565b600081518084526020840193506020830160005b828110156156ce5781516001600160a01b0316865260209586019590910190600101615c3e565b608081526000615c786080830187615c2a565b60208301959095525065ffffffffffff92909216604083015260ff16606090910152919050565b600060208284031215615cb157600080fd5b815161507581615378565b8481528360208201526060604082015281606082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115615d0157600080fd5b8260051b808560808501379190910160800195945050505050565b600060208284031215615d2e57600080fd5b5051919050565b60208152815160208201526020820151604082015261ffff604083015116606082015263ffffffff606083015116608082015263ffffffff60808301511660a0820152600060a083015160c08084015280518060e085015260005b81811015615dae576020818401810151610100878401015201615d90565b5060006101008286010152610100601f19601f8301168501019250505092915050565b6fffffffffffffffffffffffffffffffff8181168382160290811690818114615dfc57615dfc615954565b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006fffffffffffffffffffffffffffffffff831680615e5457615e54615e03565b806fffffffffffffffffffffffffffffffff84160491505092915050565b604081526000615e856040830185615c2a565b90508260208301529392505050565b600082615ea357615ea3615e03565b500690565b600082615eb757615eb7615e03565b50049056fea2646970667358221220cee75b530978bd471e6a9faccf22836a8906b15067ab8ee9d8a8ba7f100f4b1764736f6c634300081c003300000000000000000000000013503b622abc0bd30a7e9687057df6e8c42fb928000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4877b27249e78c980f8fbcecccae278bbc9f801eb504361883d2cbd1c2f0c4bc5b000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a8077df514608a09f83e4e8d300645594e5d7234665448ba83f51a50f842bd3d9
Deployed Bytecode
0x6080604052600436106102f25760003560e01c80638ac000211161018f578063b09e6621116100e1578063d547741f1161008a578063f5b541a611610064578063f5b541a614610ada578063f6f1306014610b0e578063fa2af9da14610b2e57600080fd5b8063d547741f14610a6d578063e4dafec914610a8d578063f2fde38b14610aba57600080fd5b8063c1e0d236116100bb578063c1e0d23614610a0d578063cc03c34214610a2d578063d1d06b2e14610a4d57600080fd5b8063b09e662114610957578063b5eb666614610977578063bf19d77a146109ed57600080fd5b80639854471011610143578063a694fc3a1161011d578063a694fc3a146108e7578063a942f56a146108fa578063aa7029da1461092a57600080fd5b806398544710146108925780639eccacf6146108b2578063a217fddf146108d257600080fd5b80638ea98117116101745780638ea981171461080e578063903978a51461082e57806391d148541461084e57600080fd5b80638ac00021146107da5780638da5cb5b146107f057600080fd5b80633c0da92a116102485780635c975abb116101fc5780635fba3171116101d65780635fba3171146107785780636b1426a41461079857806379ba5097146107c557600080fd5b80635c975abb146105815780635cb6dfff146105a25780635d4bc0ce1461066057600080fd5b8063409f91031161022d578063409f91031461053557806345bb327b146105555780634d2c53cd1461056b57600080fd5b80633c0da92a146104c95780633e413bee146104e957600080fd5b806328e7a854116102aa57806334d6b3661161028457806334d6b3661461048157806336566f061461049457806336568abe146104a957600080fd5b806328e7a854146104215780632a1234fb146104415780632f2ff15d1461046157600080fd5b80631c556c17116102db5780631c556c17146103a15780631fe543e3146103c3578063248a9ca3146103e357600080fd5b806301ffc9a7146102f7578063150b7a021461032c575b600080fd5b34801561030357600080fd5b5061031761031236600461507c565b610b4e565b60405190151581526020015b60405180910390f35b34801561033857600080fd5b50610370610347366004615133565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610323565b3480156103ad57600080fd5b506103c16103bc366004615212565b610be7565b005b3480156103cf57600080fd5b506103c16103de36600461523e565b610d49565b3480156103ef57600080fd5b506104136103fe3660046152bf565b60009081526020819052604090206001015490565b604051908152602001610323565b34801561042d57600080fd5b506103c161043c3660046152bf565b610db3565b34801561044d57600080fd5b506103c161045c3660046152bf565b610f8b565b34801561046d57600080fd5b506103c161047c3660046152d8565b610ff7565b6103c161048f366004615308565b611022565b3480156104a057600080fd5b506103c161152b565b3480156104b557600080fd5b506103c16104c43660046152d8565b6115d7565b3480156104d557600080fd5b506104136104e436600461533d565b611623565b3480156104f557600080fd5b5061051d7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b6040516001600160a01b039091168152602001610323565b34801561054157600080fd5b506103c1610550366004615386565b611cd2565b34801561056157600080fd5b5061041360075481565b34801561057757600080fd5b50610413600b5481565b34801561058d57600080fd5b5060055461031790600160a01b900460ff1681565b3480156105ae57600080fd5b506106186105bd3660046152bf565b6009602052600090815260409020805460019091015460ff808316926101008104821692620100008204909216916301000000820465ffffffffffff16916901000000000000000000900469ffffffffffffffffffff169086565b60408051961515875294151560208701529215159385019390935265ffffffffffff16606084015269ffffffffffffffffffff909116608083015260a082015260c001610323565b34801561066c57600080fd5b5061076461067b3660046152bf565b600c60205260009081526040902080546001909101546001600160a01b0382169160ff600160a01b820481169265ffffffffffff75010000000000000000000000000000000000000000008404169261ffff7b01000000000000000000000000000000000000000000000000000000820416927d01000000000000000000000000000000000000000000000000000000000082048116927e0100000000000000000000000000000000000000000000000000000000000090920416906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041688565b6040516103239897969594939291906153ee565b34801561078457600080fd5b506103c16107933660046152bf565b611e26565b3480156107a457600080fd5b506107b86107b33660046152bf565b6121d2565b604051610323919061545e565b3480156107d157600080fd5b506103c161226d565b3480156107e657600080fd5b5061041360065481565b3480156107fc57600080fd5b506002546001600160a01b031661051d565b34801561081a57600080fd5b506103c16108293660046154c6565b612354565b34801561083a57600080fd5b506103c16108493660046154e3565b61248b565b34801561085a57600080fd5b506103176108693660046152d8565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b34801561089e57600080fd5b506103c16108ad3660046152bf565b6127cd565b3480156108be57600080fd5b5060045461051d906001600160a01b031681565b3480156108de57600080fd5b50610413600081565b6103c16108f53660046152bf565b6127de565b34801561090657600080fd5b506103176109153660046152bf565b600d6020526000908152604090205460ff1681565b34801561093657600080fd5b506104136109453660046152bf565b600a6020526000908152604090205481565b34801561096357600080fd5b506103c16109723660046155be565b612bbe565b34801561098357600080fd5b506109de6109923660046152bf565b600e6020526000908152604090205460ff81169061010081046001600160a01b031690750100000000000000000000000000000000000000000090046affffffffffffffffffffff1683565b6040516103239392919061562f565b3480156109f957600080fd5b506103c1610a083660046152bf565b612d2e565b348015610a1957600080fd5b506103c1610a283660046152bf565b612e1b565b348015610a3957600080fd5b506103c1610a483660046154c6565b612e2c565b348015610a5957600080fd5b506103c1610a683660046152bf565b612e72565b348015610a7957600080fd5b506103c1610a883660046152d8565b612f55565b348015610a9957600080fd5b50610aad610aa83660046152bf565b612f7a565b60405161032391906157f4565b348015610ac657600080fd5b506103c1610ad53660046154c6565b61338d565b348015610ae657600080fd5b506104137f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c81565b348015610b1a57600080fd5b506103c1610b293660046152bf565b61339e565b348015610b3a57600080fd5b5060055461051d906001600160a01b031681565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b000000000000000000000000000000000000000000000000000000001480610be157507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c610c1181613481565b600554600160a01b900460ff1615610c55576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c5d61348b565b6000838152600c6020526040812060038101805491929160ff8616908110610c8757610c87615925565b60009182526020918290206040805160a0810182526002938402909201805460ff80821685526101008204811615159685019690965292949093918501926201000090041690811115610cdc57610cdc6153ab565b6002811115610ced57610ced6153ab565b81528154630100000090046001600160a01b031660208083019190915260019092015460409091015281015190915015610d39578154610d3990869083906001600160a01b03166134ce565b5050610d4460018055565b505050565b6004546001600160a01b03163314610da857600480546040517f1cf993f400000000000000000000000000000000000000000000000000000000815233928101929092526001600160a01b031660248201526044015b60405180910390fd5b610d44838383613835565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c610ddd81613481565b610de561348b565b6000828152600c60209081526040808320600a83528184205484526009909252909120805460ff16610e43576040517f1b71a84c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805462010000900460ff1615610e85576040517f3975fee200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038254600160a01b900460ff166008811115610ea457610ea46153ab565b14610edb576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80544290610efa906301000000900465ffffffffffff1661012c615983565b65ffffffffffff161115610f3a576040517fad55303b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805460ff1916905580547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674020000000000000000000000000000000000000000179055505060018055565b5050565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c610fb581613481565b506000908152600c6020908152604080832080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff169055600e909152812055565b60008281526020819052604090206001015461101281613481565b61101c83836139a9565b50505050565b600554600160a01b900460ff1615611066576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61106e61348b565b6000838152600c6020526040902060028154600160a01b900460ff16600881111561109b5761109b6153ab565b146110d2576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80547e01000000000000000000000000000000000000000000000000000000000000900460ff1615611108576111088433613a53565b6002810154808410611146576040517f1dbcf8aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082600201858154811061115d5761115d615925565b600091825260209182902060408051606081018252929091015465ffffffffffff80821684526601000000000000820416938301939093526c010000000000000000000000009092046001600160a01b0316918101829052915034146111ef576040517fad80e5f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80604001516001600160a01b0316600003611296576040805133602082015290810187905260009060600160408051601f1981840301815291815281516020928301206000818152600d90935291205490915060ff161561127c576040517fd72456a200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000908152600d60205260409020805460ff191660011790555b82547501000000000000000000000000000000000000000000900465ffffffffffff166000036113685733836004016000815481106112d7576112d7615925565b9060005260206000200160000160066101000a8154816001600160a01b0302191690836001600160a01b0316021790555080602001518360040160008154811061132357611323615925565b600091825260209091200180547fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000001665ffffffffffff9290921691909117905561140f565b6000604051806040016040528083602001518660000160159054906101000a900465ffffffffffff1661139b9190615983565b65ffffffffffff90811682523360209283015260048701805460018101825560009182529083902084519101805494909301516001600160a01b03166601000000000000027fffffffffffff0000000000000000000000000000000000000000000000000000909416911617919091179055505b6001830180543491906000906114389084906fffffffffffffffffffffffffffffffff166159a1565b92506101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555080602001518360000160158282829054906101000a900465ffffffffffff166114979190615983565b82546101009290920a65ffffffffffff818102199093169183160217909155845460408051750100000000000000000000000000000000000000000090920490921681526020810188905260ff87169181019190915233915087907f0741232ab3faf4ebed1295982370d6148eb0ecbc8e76cf8a3492a337b84cff239060600160405180910390a3505050610d4460018055565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c61155581613481565b6005805460ff600160a01b80830482161581027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90931692909217928390556040517f9077d36bc00859b5c3f320310707208543dd35092cb0a0fe117d0c6a558b148b936115cc9390049091161515815260200190565b60405180910390a150565b6001600160a01b0381163314611619576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d448282613c44565b60007f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c61164f81613481565b61165761348b565b600061166660408501856159c9565b9150600090506116796060860186615a38565b90509050816000036116b7576040517f87a097f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8015806116c45750600a81115b156116fb576040517fab13062d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61271061170b6020870187615a9f565b61ffff161115611747576040517fb9d3a94500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600b6000815461175890615ac3565b91829055506000818152600c6020908152604090912080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16600160a01b1781559192506117a990880188615a9f565b815461ffff919091167b01000000000000000000000000000000000000000000000000000000027fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161781556118076040880160208901615afb565b6001820180546fffffffffffffffffffffffffffffffff92831670010000000000000000000000000000000002921691909117905560005b838110156119ed57600061185660608a018a615a38565b8381811061186657611866615925565b90506060020180360381019061187c9190615b2d565b9050600281516002811115611893576118936153ab565b11806118bf575060408101511580156118bf57506002815160028111156118bc576118bc6153ab565b14155b806118f357506000815160028111156118da576118da6153ab565b141580156118f3575060208101516001600160a01b0316155b1561192a576040517f5f12e2ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60038301805460018101825560009182526020909120600291820201805460ff851660ff198216811783558451929384927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff00169091179062010000908490811115611997576119976153ab565b0217905550602082015181546001600160a01b039091166301000000027fffffffffffffffffff0000000000000000000000000000000000000000ffffff9091161781556040909101516001918201550161183f565b5060005b84811015611bc1576000611a0860408a018a6159c9565b83818110611a1857611a18615925565b905060400201803603810190611a2e9190615b98565b805190915065ffffffffffff161580611a5c575060208101516001600160a01b0316158015611a5c57508115155b15611a93576040517f1dbcf8aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082118015611aee575060028301611aad600184615c04565b81548110611abd57611abd615925565b60009182526020918290200154908201516001600160a01b036c010000000000000000000000009092048216911611155b15611b25576040517f1dbcf8aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002830180546001818101835560009283526020928390209091018054845194909301516001600160a01b03166c01000000000000000000000000026bffffffffffffffffffffffff65ffffffffffff9586166601000000000000027fffffffffffffffffffffffffffffffffffffffff00000000000000000000000090951695871695909517939093179390931691909117909155016119f1565b50604080518082019091526000808252602080830182815260048501805460018101825581855292842085519301805492516001600160a01b03166601000000000000027fffffffffffff000000000000000000000000000000000000000000000000000090931665ffffffffffff9490941693909317919091179091558054909190611c5057611c50615925565b60009182526020918290200180547fffffffffffff00000000000000000000000000000000000000000000000000001690556040805187815291820186905284917f9aa2065fb625f0039ef34f9a9cb1fe8e45055c5aa4d830c016505ee55a598a84910160405180910390a250909450505050611ccc60018055565b50919050565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c611cfc81613481565b600554600160a01b900460ff1615611d40576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d4861348b565b6000838152600c6020526040902060028154600160a01b900460ff166008811115611d7557611d756153ab565b14611dac576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018101546fffffffffffffffffffffffffffffffff7001000000000000000000000000000000008204811691161015611e12576040517f2cc7ed9d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e1c8484613cc7565b50610d4460018055565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c611e5081613481565b611e5861348b565b6000828152600c6020526040902060058154600160a01b900460ff166008811115611e8557611e856153ab565b1480611ead575060068154600160a01b900460ff166008811115611eab57611eab6153ab565b145b80611ed4575060078154600160a01b900460ff166008811115611ed257611ed26153ab565b145b80611efb575060088154600160a01b900460ff166008811115611ef957611ef96153ab565b145b15611f32576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028154600160a01b900460ff166008811115611f5157611f516153ab565b1480611f79575060038154600160a01b900460ff166008811115611f7757611f776153ab565b145b156121395760005b600382015481101561204d576000826003018281548110611fa457611fa4615925565b60009182526020918290206040805160a0810182526002938402909201805460ff80821685526101008204811615159685019690965292949093918501926201000090041690811115611ff957611ff96153ab565b600281111561200a5761200a6153ab565b815281546001600160a01b0363010000009091048116602083015260019092015460409091015284549192506120449187918491166134ce565b50600101611f81565b5060055460018201546040516000926001600160a01b0316916fffffffffffffffffffffffffffffffff16908381818185875af1925050503d80600081146120b1576040519150601f19603f3d011682016040523d82523d6000602084013e6120b6565b606091505b50509050806120f1576040517f3022f2e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5080547fffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167d0100000000000000000000000000000000000000000000000000000000001781555b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740800000000000000000000000000000000000000001781556001810154604080516fffffffffffffffffffffffffffffffff9092168252426020830152805185927f50d7e951bb520133496ca2822d7c0578a201ff4bbbb79ed9a5e773fdc24ca10b92908290030190a250610f8760018055565b6060600c6000838152602001908152602001600020600501805480602002602001604051908101604052809291908181526020016000905b828210156122625760008481526020908190206040805160608101825260028602909201805460ff8116845261010090046001600160a01b03168385015260019081015491830191909152908352909201910161220a565b505050509050919050565b6003546001600160a01b031633146122e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610d9f565b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000808216339081179093556003805490911690556040516001600160a01b03909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b6002546001600160a01b0316331480159061237a57506004546001600160a01b03163314155b156123e557336123926002546001600160a01b031690565b600480546040517f061db9c10000000000000000000000000000000000000000000000000000000081526001600160a01b0394851692810192909252918316602482015291166044820152606401610d9f565b6001600160a01b038116612425576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fd1a6a14209a385a964d036e404cb5cfb71f4000cdb03c9366292430787261be6906020016115cc565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c6124b581613481565b600554600160a01b900460ff16156124f9576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61250161348b565b6000848152600c6020526040902060028154600160a01b900460ff16600881111561252e5761252e6153ab565b14612565576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83516000805b8281101561271557600087828151811061258757612587615925565b6020026020010151905060006040518060400160405280848860000160159054906101000a900465ffffffffffff1665ffffffffffff166125c89190615c17565b6125d3906001615c17565b65ffffffffffff90811682526001600160a01b038516602090920191909152875491925075010000000000000000000000000000000000000000009091041615801561261d575082155b1561269b57808660040160008154811061263957612639615925565b600091825260209182902083519101805493909201516001600160a01b03166601000000000000027fffffffffffff000000000000000000000000000000000000000000000000000090931665ffffffffffff90911617919091179055612707565b6004860180546001810182556000918252602091829020835191018054928401516001600160a01b03166601000000000000027fffffffffffff000000000000000000000000000000000000000000000000000090931665ffffffffffff909216919091179190911790555b50506001918201910161256b565b5082548190849060159061274b9084907501000000000000000000000000000000000000000000900465ffffffffffff16615983565b92506101000a81548165ffffffffffff021916908365ffffffffffff160217905550867f43b4167774874553cc57c1c23bbc4589ed22b35aba69867e65720bbabbd3c23c87848660000160159054906101000a900465ffffffffffff16896040516127b99493929190615c65565b60405180910390a250505061101c60018055565b60006127d881613481565b50600755565b6127e661348b565b6000818152600c60205260409020600381015460018254600160a01b900460ff166008811115612818576128186153ab565b1461284f576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81547fffffffffffffffffffffffff000000000000000000000000000000000000000016331782556000805b82811015612b0157600084600301828154811061289a5761289a615925565b60009182526020918290206040805160a0810182526002938402909201805460ff808216855261010082048116151596850196909652929490939185019262010000900416908111156128ef576128ef6153ab565b6002811115612900576129006153ab565b81528154630100000090046001600160a01b03166020820152600190910154604090910152905060008160400151600281111561293f5761293f6153ab565b0361295a5760808101516129539084615c17565b9250612af8565b600181604001516002811115612972576129726153ab565b03612a1957606081015160808201516040517f23b872dd00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101919091526001600160a01b03909116906323b872dd906064016020604051808303816000875af11580156129ef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a139190615c9f565b50612af8565b600281604001516002811115612a3157612a316153ab565b03612ac657606081015160808201516040517f42842e0e00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101919091526001600160a01b03909116906342842e0e90606401600060405180830381600087803b158015612aa957600080fd5b505af1158015612abd573d6000803e3d6000fd5b50505050612af8565b6040517f5f12e2ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5060010161287b565b5082547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674020000000000000000000000000000000000000000178355348114612b78576040517fd8bd2c2500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051428152339085907f8520a64b531077689214cabcfa489eef68e4b29ffcb6f1e22cd8fe4cffe706de9060200160405180910390a3505050612bbb60018055565b50565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c612be881613481565b6000858152600c6020526040902080547fff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167e010000000000000000000000000000000000000000000000000000000000001781556001600160a01b0384161580612c5e57506affffffffffffffffffffff8316155b15612c95576040517f8826122a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000868152600e6020526040902080548690829060ff19166001836002811115612cc157612cc16153ab565b021790555080546affffffffffffffffffffff90941675010000000000000000000000000000000000000000000274ffffffffffffffffffffffffffffffffffffffffff6001600160a01b03909616610100029590951660ff909416939093179390931790915550505050565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c612d5881613481565b600554600160a01b900460ff1615612d9c576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612da461348b565b6000828152600c6020526040902060068154600160a01b900460ff166008811115612dd157612dd16153ab565b14612e08576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612e1183613ee4565b50610f8760018055565b6000612e2681613481565b50600655565b6000612e3781613481565b50600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c612e9c81613481565b600554600160a01b900460ff1615612ee0576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612ee861348b565b6000828152600c6020526040902060048154600160a01b900460ff166008811115612f1557612f156153ab565b14612f4c576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612e1183614153565b600082815260208190526040902060010154612f7081613481565b61101c8383613c44565b6040805161018080820183526000808352602080840182905283850182905260608085018390526080850183905260a0850183905260c0850183905260e08501839052610100850181905261012085018190526101408501819052610160850152858252600c815290849020845192830190945283546001600160a01b03811683529293919290830190600160a01b900460ff16600881111561301f5761301f6153ab565b6008811115613030576130306153ab565b8152815465ffffffffffff750100000000000000000000000000000000000000000082041660208084019190915261ffff7b0100000000000000000000000000000000000000000000000000000083041660408085019190915260ff7d01000000000000000000000000000000000000000000000000000000000084048116151560608601527e010000000000000000000000000000000000000000000000000000000000009093049092161515608084015260018401546fffffffffffffffffffffffffffffffff80821660a08601527001000000000000000000000000000000009091041660c084015260028401805483518184028101840190945280845260e090940193909160009084015b828210156131a6576000848152602090819020604080516060810182529185015465ffffffffffff80821684526601000000000000820416838501526c0100000000000000000000000090046001600160a01b03169082015282526001909201910161313f565b50505050815260200160038201805480602002602001604051908101604052809291908181526020016000905b8282101561327c5760008481526020908190206040805160a0810182526002808702909301805460ff8082168452610100820481161515968401969096529194909392850192620100009092041690811115613231576132316153ab565b6002811115613242576132426153ab565b81528154630100000090046001600160a01b03166020808301919091526001928301546040909201919091529183529290920191016131d3565b50505050815260200160048201805480602002602001604051908101604052809291908181526020016000905b828210156132fa576000848152602090819020604080518082019091529084015465ffffffffffff81168252660100000000000090046001600160a01b0316818301528252600190920191016132a9565b50505050815260200160058201805480602002602001604051908101604052809291908181526020016000905b8282101561337f5760008481526020908190206040805160608101825260028602909201805460ff8116845261010090046001600160a01b031683850152600190810154918301919091529083529092019101613327565b505050915250909392505050565b6133956149bb565b612bbb81614a31565b7f523a704056dcd17bcf83bed8b68c59416dac1119be77755efe3bde0a64e46e0c6133c881613481565b600554600160a01b900460ff161561340c576040517f9e87fac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61341461348b565b6000828152600c6020526040902060058154600160a01b900460ff166008811115613441576134416153ab565b14613478576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612e1183614b0d565b612bbb8133614d9a565b6002600154036134c7576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600155565b600080836040015160028111156134e7576134e76153ab565b1480156134f8575060008360800151115b156135cf5760808301516040516001600160a01b0384169190600081818185875af1925050503d806000811461354a576040519150601f19603f3d011682016040523d82523d6000602084013e61354f565b606091505b505080915050806135ca576000848152600c60205260409020835160038201805460019260ff1690811061358557613585615925565b600091825260209091206002909102018054911515610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff909216919091179055505b613789565b6001836040015160028111156135e7576135e76153ab565b036136b457606083015160808401516040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b038581166004830152602482019290925291169063a9059cbb906044016020604051808303816000875af192505050801561367b575060408051601f3d908101601f1916820190925261367891810190615c9f565b60015b6136aa576000848152600c60205260409020835160038201805460019260ff1690811061358557613585615925565b5060019050613789565b6002836040015160028111156136cc576136cc6153ab565b0361378957606083015160808401516040517f42842e0e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03858116602483015260448201929092529116906342842e0e90606401600060405180830381600087803b15801561374557600080fd5b505af1925050508015613756575060015b613785576000848152600c60205260409020835160038201805460019260ff1690811061358557613585615925565b5060015b80156137e05782516040805186815260ff90921660208301526001600160a01b03841682820152517f093c0e8b72bba93a9b348d23f07bdb7a9580015810b3e267daa431adf75a4ae99181900360600190a161101c565b82516040805186815260ff90921660208301526001600160a01b038416908201527f708a752d5a96615e830b607b8d5ffee8c131d33afa7699a5b571b952a93431b1906060015b60405180910390a150505050565b6000838152600960205260409020805460ff161561101c5780546901000000000000000000900469ffffffffffffffffffff166000818152600c60205260409081902090517f777b0ab3fa10335e90b469ef6a82310e1192541fc44fb3acaf2cc84a3dee474d906138ad908490899089908990615cbc565b60405180910390a160038154600160a01b900460ff1660088111156138d4576138d46153ab565b036139a15782547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffff1662010000178355848460008161391557613915615925565b602002919091013560018501555080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740400000000000000000000000000000000000000001781558254610100900460ff161580156139815750600554600160a01b900460ff16155b156139a15761398f82614153565b61399882614b0d565b6139a182613ee4565b505050505050565b6000828152602081815260408083206001600160a01b038516845290915281205460ff16613a4b576000838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055613a033390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610be1565b506000610be1565b6000828152600e60205260408082208151606081019092528054829060ff166002811115613a8357613a836153ab565b6002811115613a9457613a946153ab565b8152905461010081046001600160a01b03166020830152750100000000000000000000000000000000000000000090046affffffffffffffffffffff166040909101529050600181516002811115613aee57613aee6153ab565b03613bc957602081015160408083015190517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301526affffffffffffffffffffff909216918316906370a08231906024015b602060405180830381865afa158015613b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b919190615d1c565b101561101c576040517fccb3bd8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600281516002811115613bde57613bde6153ab565b03610d4457602081015160408083015190517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0385811660048301526affffffffffffffffffffff909216918316906370a0823190602401613b50565b6000828152602081815260408083206001600160a01b038516845290915281205460ff1615613a4b576000838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610be1565b6000828152600c6020908152604080832080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674030000000000000000000000000000000000000000178155600454825160c08101845260075481526006548186015260085461ffff6401000000008204168286015263ffffffff808216606084015266010000000000009091041660808201528351948501909352600184529093926001600160a01b0390911691639b1c385e9160a0820190613d8c90614e06565b8152506040518263ffffffff1660e01b8152600401613dab9190615d35565b6020604051808303816000875af1158015613dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dee9190615d1c565b6000818152600960209081526040808320805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009091166101008a15150217177fffffffffffffffffffffffffff00000000000000000000000000000000ffffff1663010000004265ffffffffffff16027fffffffffffffffffffffffffff00000000000000000000ffffffffffffffffff1617690100000000000000000069ffffffffffffffffffff8b1602179055878352600a82529182902083905581518781529081018390529192507f3d94fecedaa4f90b8bd459797adb95f5bb11426025c5541390d9ccc1ad1b60a19101613827565b6000818152600c6020526040902080547d010000000000000000000000000000000000000000000000000000000000900460ff1615613f4f576040517f45a1ea2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8054600182015460009161271091613f9c917b01000000000000000000000000000000000000000000000000000000900461ffff16906fffffffffffffffffffffffffffffffff16615dd1565b613fa69190615e32565b60018301546fffffffffffffffffffffffffffffffff9182169250600091613fd091849116615c04565b6005546040519192506000916001600160a01b039091169084908381818185875af1925050503d8060008114614022576040519150601f19603f3d011682016040523d82523d6000602084013e614027565b606091505b505084546040519192506000916001600160a01b039091169084908381818185875af1925050503d806000811461407a576040519150601f19603f3d011682016040523d82523d6000602084013e61407f565b606091505b5050905081158061408e575080155b156140c5576040517f3022f2e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffff00ffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167d010000000000000000070000000000000000000000000000000000000000178555604080518781526020810186905280820185905290517ff22c03b9832b79146cb366b733d5f9b0a1cf3feb2c8bba254fb91e33f278358f9181900360600190a1505050505050565b6000818152600a60209081526040808320548084526009835292819020815160c081018352815460ff808216151580845261010083048216151596840196909652620100008204161515938201939093526301000000830465ffffffffffff166060820152690100000000000000000090920469ffffffffffffffffffff1660808301526001015460a082015290614217576040517f1b71a84c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000838152600c6020526040902060048154600160a01b900460ff166008811115614244576142446153ab565b1461427b576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381015460048201805490600090614295600184615c04565b815481106142a5576142a5615925565b600091825260208220015465ffffffffffff811692506142d09060081c64ffffffffff166001615c17565b67ffffffffffffffff8111156142e8576142e86150d3565b604051908082528060200260200182016040528015614311578160200160208202803683370190505b5060a08701519091506000808667ffffffffffffffff811115614336576143366150d3565b60405190808252806020026020018201604052801561435f578160200160208202803683370190505b5090506000805b888110156149335760008a600301828154811061438557614385615925565b60009182526020918290206040805160a0810182526002938402909201805460ff808216855261010082048116151596850196909652929490939185019262010000900416908111156143da576143da6153ab565b60028111156143eb576143eb6153ab565b81528154630100000090046001600160a01b03166020820152600190910154604090910152905087831061455e578a5484516001600160a01b039091169085908490811061443b5761443b615925565b60200260200101906001600160a01b031690816001600160a01b0316815250508a6005016040518060600160405280836000015160ff1681526020018d60000160009054906101000a90046001600160a01b03166001600160a01b0316815260200188815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816001015550508160010191508560405160200161453d91815260200190565b6040516020818303038152906040528051906020012060001c955050614366565b614569868989614ea4565b985090965094508261457a81615ac3565b93506000905060048c0161458e8188614f74565b8154811061459e5761459e615925565b600091825260209091200154660100000000000090046001600160a01b031690506001826040015160028111156145d7576145d76153ab565b14801561461957507f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b031682606001516001600160a01b0316145b80156146c357506040517ffe575a870000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169063fe575a8790602401602060405180830381865afa15801561469f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146c39190615c9f565b15614813575b6040517ffe575a870000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169063fe575a8790602401602060405180830381865afa158015614748573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061476c9190615c9f565b156148135788841061478957508a546001600160a01b0316614813565b6040805160208101899052016040516020818303038152906040528051906020012060001c96506147bb878a8a614ea4565b99509097509550836147cc81615ac3565b94505060048c016147dd8188614f74565b815481106147ed576147ed615925565b600091825260209091200154660100000000000090046001600160a01b031690506146c9565b8085848151811061482657614826615925565b60200260200101906001600160a01b031690816001600160a01b0316815250508b6005016040518060600160405280846000015160ff168152602001836001600160a01b0316815260200189815250908060018154018082558091505060019003906000526020600020906002020160009091909190915060008201518160000160006101000a81548160ff021916908360ff16021790555060208201518160000160016101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816001015550508660405160200161490b91815260200190565b6040516020818303038152906040528051906020012060001c96508260010192505050614366565b5088547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740500000000000000000000000000000000000000001789556040518c907f61f33e70c53ae655df89109e261f76ef392f32df37ef1525cb02378613f93de1906149a59085904290615e72565b60405180910390a2505050505050505050505050565b6002546001600160a01b03163314614a2f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610d9f565b565b336001600160a01b03821603614aa3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610d9f565b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03838116918217909255600254604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b6000818152600c6020526040902060058154600160a01b900460ff166008811115614b3a57614b3a6153ab565b14614b71576040517ff525e32000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b6005820154811015614d59576000826005018281548110614b9757614b97615925565b6000918252602091829020604080516060810182526002909302909101805460ff811684526001600160a01b036101009091041693830184905260010154908201529150614c9957614c948484600301836000015160ff1681548110614bff57614bff615925565b60009182526020918290206040805160a0810182526002938402909201805460ff80821685526101008204811615159685019690965292949093918501926201000090041690811115614c5457614c546153ab565b6002811115614c6557614c656153ab565b815281546001600160a01b036301000000909104811660208301526001909201546040909101528654166134ce565b614d50565b614d508484600301836000015160ff1681548110614cb957614cb9615925565b60009182526020918290206040805160a0810182526002938402909201805460ff80821685526101008204811615159685019690965292949093918501926201000090041690811115614d0e57614d0e6153ab565b6002811115614d1f57614d1f6153ab565b81528154630100000090046001600160a01b03166020808301919091526001909201546040909101528401516134ce565b50600101614b74565b5080547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167406000000000000000000000000000000000000000017905550565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610f87576040517fe2517d3f0000000000000000000000000000000000000000000000000000000081526001600160a01b038216600482015260248101839052604401610d9f565b60607f92fd13387c7fe7befbc38d303d6468778fb9731bc4583f17d92989c6fcfdeaaa82604051602401614e3f91511515815260200190565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915292915050565b600080606081614eb48688615e94565b614ebf906001615c17565b9050600881901c600160ff83161b5b80878381518110614ee157614ee1615925565b602002602001015116600014614f415760408051602081018b90520160408051601f1981840301815291905280516020909101209850614f21888a615e94565b614f2c906001615c17565b92505050600881901c600160ff83161b614ece565b80878381518110614f5457614f54615925565b602090810291909101018051919091179052509697909650939450505050565b81546000908103614f8757506000610be1565b82546000905b80821015614fe5576000614fa1838361505a565b905084868281548110614fb657614fb6615925565b60009182526020909120015465ffffffffffff161115614fd857809150614fdf565b8060010192505b50614f8d565b60008211801561502357508385614ffd600185615c04565b8154811061500d5761500d615925565b60009182526020909120015465ffffffffffff16145b1561505257507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019050610be1565b509050610be1565b60006150696002848418615ea8565b61507590848416615c17565b9392505050565b60006020828403121561508e57600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461507557600080fd5b6001600160a01b0381168114612bbb57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561512b5761512b6150d3565b604052919050565b6000806000806080858703121561514957600080fd5b8435615154816150be565b93506020850135615164816150be565b925060408501359150606085013567ffffffffffffffff81111561518757600080fd5b8501601f8101871361519857600080fd5b803567ffffffffffffffff8111156151b2576151b26150d3565b6151c56020601f19601f84011601615102565b8181528860208385010111156151da57600080fd5b8160208401602083013760006020838301015280935050505092959194509250565b803560ff8116811461520d57600080fd5b919050565b6000806040838503121561522557600080fd5b82359150615235602084016151fc565b90509250929050565b60008060006040848603121561525357600080fd5b83359250602084013567ffffffffffffffff81111561527157600080fd5b8401601f8101861361528257600080fd5b803567ffffffffffffffff81111561529957600080fd5b8660208260051b84010111156152ae57600080fd5b939660209190910195509293505050565b6000602082840312156152d157600080fd5b5035919050565b600080604083850312156152eb57600080fd5b8235915060208301356152fd816150be565b809150509250929050565b60008060006060848603121561531d57600080fd5b8335925060208401359150615334604085016151fc565b90509250925092565b60006020828403121561534f57600080fd5b813567ffffffffffffffff81111561536657600080fd5b82016080818503121561507557600080fd5b8015158114612bbb57600080fd5b6000806040838503121561539957600080fd5b8235915060208301356152fd81615378565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600981106153ea576153ea6153ab565b9052565b6001600160a01b0389168152610100810161540c602083018a6153da565b65ffffffffffff97909716604082015261ffff959095166060860152921515608085015290151560a08401526fffffffffffffffffffffffffffffffff90811660c08401521660e09091015292915050565b602080825282518282018190526000918401906040840190835b818110156154bb578351805160ff1684526020808201516001600160a01b0316908501526040908101519084015260608301602094909401939250600101615478565b509095945050505050565b6000602082840312156154d857600080fd5b8135615075816150be565b6000806000606084860312156154f857600080fd5b83359250602084013567ffffffffffffffff81111561551657600080fd5b8401601f8101861361552757600080fd5b803567ffffffffffffffff811115615541576155416150d3565b8060051b61555160208201615102565b9182526020818401810192908101908984111561556d57600080fd5b6020850194505b8385101561559b5784359250615589836150be565b82825260209485019490910190615574565b8096505050505050615334604085016151fc565b80356003811061520d57600080fd5b600080600080608085870312156155d457600080fd5b843593506155e4602086016155af565b925060408501356155f4816150be565b915060608501356affffffffffffffffffffff8116811461561457600080fd5b939692955090935050565b600381106153ea576153ea6153ab565b6060810161563d828661561f565b6001600160a01b03841660208301526affffffffffffffffffffff83166040830152949350505050565b600081518084526020840193506020830160005b828110156156ce57815165ffffffffffff815116875265ffffffffffff60208201511660208801526001600160a01b0360408201511660408801525060608601955060208201915060018101905061567b565b5093949350505050565b600081518084526020840193506020830160005b828110156156ce57815160ff8151168752602081015115156020880152604081015161571b604089018261561f565b506060818101516001600160a01b0316908801526080908101519087015260a090950194602091909101906001016156ec565b600081518084526020840193506020830160005b828110156156ce578151805165ffffffffffff1687526020908101516001600160a01b03168188015260409096019590910190600101615762565b600081518084526020840193506020830160005b828110156156ce578151805160ff1687526020808201516001600160a01b03169088015260409081015190870152606086019550602091909101906001016157b1565b6020815261580e6020820183516001600160a01b03169052565b6000602083015161582260408401826153da565b50604083015165ffffffffffff8116606084015250606083015161ffff8116608084015250608083015180151560a08401525060a083015180151560c08401525060c08301516fffffffffffffffffffffffffffffffff811660e08401525060e08301516fffffffffffffffffffffffffffffffff8116610100840152506101008301516101806101208401526158bd6101a0840182615667565b9050610120840151601f19848303016101408501526158dc82826156d8565b915050610140840151601f19848303016101608501526158fc828261574e565b915050610160840151601f198483030161018085015261591c828261579d565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b65ffffffffffff8181168382160190811115610be157610be1615954565b6fffffffffffffffffffffffffffffffff8181168382160190811115610be157610be1615954565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126159fe57600080fd5b83018035915067ffffffffffffffff821115615a1957600080fd5b6020019150600681901b3603821315615a3157600080fd5b9250929050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615a6d57600080fd5b83018035915067ffffffffffffffff821115615a8857600080fd5b6020019150606081023603821315615a3157600080fd5b600060208284031215615ab157600080fd5b813561ffff8116811461507557600080fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615af457615af4615954565b5060010190565b600060208284031215615b0d57600080fd5b81356fffffffffffffffffffffffffffffffff8116811461507557600080fd5b60006060828403128015615b4057600080fd5b506040516060810167ffffffffffffffff81118282101715615b6457615b646150d3565b604052615b70836155af565b81526020830135615b80816150be565b60208201526040928301359281019290925250919050565b60006040828403128015615bab57600080fd5b506040805190810167ffffffffffffffff81118282101715615bcf57615bcf6150d3565b604052823565ffffffffffff81168114615be857600080fd5b81526020830135615bf8816150be565b60208201529392505050565b81810381811115610be157610be1615954565b80820180821115610be157610be1615954565b600081518084526020840193506020830160005b828110156156ce5781516001600160a01b0316865260209586019590910190600101615c3e565b608081526000615c786080830187615c2a565b60208301959095525065ffffffffffff92909216604083015260ff16606090910152919050565b600060208284031215615cb157600080fd5b815161507581615378565b8481528360208201526060604082015281606082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115615d0157600080fd5b8260051b808560808501379190910160800195945050505050565b600060208284031215615d2e57600080fd5b5051919050565b60208152815160208201526020820151604082015261ffff604083015116606082015263ffffffff606083015116608082015263ffffffff60808301511660a0820152600060a083015160c08084015280518060e085015260005b81811015615dae576020818401810151610100878401015201615d90565b5060006101008286010152610100601f19601f8301168501019250505092915050565b6fffffffffffffffffffffffffffffffff8181168382160290811690818114615dfc57615dfc615954565b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006fffffffffffffffffffffffffffffffff831680615e5457615e54615e03565b806fffffffffffffffffffffffffffffffff84160491505092915050565b604081526000615e856040830185615c2a565b90508260208301529392505050565b600082615ea357615ea3615e03565b500690565b600082615eb757615eb7615e03565b50049056fea2646970667358221220cee75b530978bd471e6a9faccf22836a8906b15067ab8ee9d8a8ba7f100f4b1764736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000013503b622abc0bd30a7e9687057df6e8c42fb928000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4877b27249e78c980f8fbcecccae278bbc9f801eb504361883d2cbd1c2f0c4bc5b000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a8077df514608a09f83e4e8d300645594e5d7234665448ba83f51a50f842bd3d9
-----Decoded View---------------
Arg [0] : _platformWallet (address): 0x13503B622abC0bD30A7e9687057DF6E8c42Fb928
Arg [1] : _usdc (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [2] : _subscriptionId (uint256): 54140516550483869840566525543971321444736077346701696483357697450419025132635
Arg [3] : _vrfCoordinator (address): 0xD7f86b4b8Cae7D942340FF628F82735b7a20893a
Arg [4] : _keyHash (bytes32): 0x8077df514608a09f83e4e8d300645594e5d7234665448ba83f51a50f842bd3d9
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000013503b622abc0bd30a7e9687057df6e8c42fb928
Arg [1] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [2] : 77b27249e78c980f8fbcecccae278bbc9f801eb504361883d2cbd1c2f0c4bc5b
Arg [3] : 000000000000000000000000d7f86b4b8cae7d942340ff628f82735b7a20893a
Arg [4] : 8077df514608a09f83e4e8d300645594e5d7234665448ba83f51a50f842bd3d9
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.