Source Code
Overview
MON Balance
MON Value
$0.03 (@ $0.02/MON)Latest 25 from a total of 42 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Distribute All P... | 37662759 | 61 days ago | IN | 0 MON | 0.05355673 | ||||
| Draw Winning Tic... | 37662748 | 61 days ago | IN | 0 MON | 0.05707461 | ||||
| Enumerate Ticket... | 37662733 | 61 days ago | IN | 0 MON | 0.03209634 | ||||
| Start Draw | 37662705 | 61 days ago | IN | 0.4 MON | 0.04037935 | ||||
| Distribute All P... | 37662332 | 61 days ago | IN | 0 MON | 0.08282471 | ||||
| Draw Winning Tic... | 37662321 | 61 days ago | IN | 0 MON | 0.10047214 | ||||
| Enumerate Ticket... | 37662309 | 61 days ago | IN | 0 MON | 0.03209634 | ||||
| Update Participa... | 37662209 | 61 days ago | IN | 0 MON | 0.1024 | ||||
| Start Draw | 37661986 | 61 days ago | IN | 2.304 MON | 0.04037935 | ||||
| Update Participa... | 37661764 | 61 days ago | IN | 0.1 MON | 0.102 | ||||
| Update Participa... | 37661711 | 61 days ago | IN | 0.2 MON | 0.102102 | ||||
| Update Participa... | 37661354 | 61 days ago | IN | 0.3 MON | 0.102 | ||||
| Update Raffle | 37660234 | 61 days ago | IN | 0 MON | 0.01539139 | ||||
| Update Raffle | 37659927 | 61 days ago | IN | 0 MON | 0.01498788 | ||||
| Update Raffle | 37659901 | 61 days ago | IN | 0 MON | 0.01498788 | ||||
| Update Raffle | 37659304 | 61 days ago | IN | 0 MON | 0.01498788 | ||||
| Update Raffle | 37659275 | 61 days ago | IN | 0 MON | 0.01498788 | ||||
| Create Raffle | 37657973 | 61 days ago | IN | 0 MON | 0.02950727 | ||||
| Create Raffle | 37657828 | 61 days ago | IN | 0 MON | 0.02950727 | ||||
| Update Participa... | 37559791 | 61 days ago | IN | 0 MON | 0.00715669 | ||||
| Distribute All P... | 37556597 | 61 days ago | IN | 0 MON | 0.09289884 | ||||
| Draw Winning Tic... | 37556042 | 61 days ago | IN | 0 MON | 0.10933094 | ||||
| Enumerate Ticket... | 37556020 | 61 days ago | IN | 0 MON | 0.03125443 | ||||
| Start Draw | 37555851 | 61 days ago | IN | 0.4 MON | 0.04184835 | ||||
| Update Participa... | 37555686 | 61 days ago | IN | 0.2 MON | 0.100001 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 37662759 | 61 days ago | 0.098 MON | ||||
| 37662759 | 61 days ago | 0.098 MON | ||||
| 37662759 | 61 days ago | 0.098 MON | ||||
| 37662759 | 61 days ago | 0.098 MON | ||||
| 37662759 | 61 days ago | 0.098 MON | ||||
| 37662705 | 61 days ago | 0.4 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37662332 | 61 days ago | 0.0975 MON | ||||
| 37661986 | 61 days ago | 2.304 MON | ||||
| 37556597 | 61 days ago | 0.098 MON | ||||
| 37556597 | 61 days ago | 0.098 MON | ||||
| 37556597 | 61 days ago | 0.098 MON | ||||
| 37556597 | 61 days ago | 0.098 MON | ||||
| 37556597 | 61 days ago | 0.098 MON | ||||
| 37556597 | 61 days ago | 0.098 MON | ||||
| 37556597 | 61 days ago | 0.098 MON | ||||
| 37556597 | 61 days ago | 0.098 MON |
Loading...
Loading
Contract Name:
Minthub
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.30;
/**
* @title Minthub.sol.
*
* version 0.1.0
*
* @author abraxas https://abraxaslabs.io
*
* @notice Abraxas is the author of the code for this protocol. It is not the owner of any
* deployed contract on any chain. Abraxas has no admin access to any deployed version of
* this protocol on mainnet chains. For all enquiries about the operation of this protocol
* please refer to the owner of the deployed contract.
*
* @notice For teams running a MintHub: The DEFAULT_ADMIN_OWNER and PLATFORM_ADMIN_OWNER
* roles on this contract have considerable operational power and should be appropriately
* secured, for example by using a MultiSig wallet with an appropriate number of trusted
* parties operating as signers.
*
* @dev Minthub is a flexible raffle contract. Users deposit a balance to the contract.
* The contract administers a series of raffles where users have a randomised chance to win
* an ERC721 held in a treasury address. Users can withdraw their balance at any time with
* the exception of when a draw is taking place. At the start of a draw the user receives
* a number of tickets in that raffle equal to the amount afforded by their balance. If for
* example the user has a balance of 115 and each ticket costs 50 they will have 2 tickets
* in that raffle (there are no partial tickets, so the additional 15 is not used in getting
* a ticket).
*
* If the user's ticket wins a prize the cost of the ticket is deducted from their balance and
* the user is sent one of the NFTs held at the treasury address. If the user does not win
* a prize there is no deduction from their balance and they do not receive an NFT.
*
* Withdrawals and deposits are paused during the draw window. This draw window automatically ends
* a set period of time after it begins, ensuring there are no admin actions required to unlock
* the withdrawal of funds.
*
* Users are "auto-entered" into every raffle where their balance is sufficient to buy a ticket(s)
* unless they have explicitly opted out of participation in a specific raffle, which they can do
* by setting participation details. Users can withdraw their balance at any time with the exception
* of while a draw is taking place. A fee is deducted from withdrawals to cover the cost of operating
* the platform. This fee is set by the platform admins and is deducted from all withdrawals.
*
* Admin Authorities
* ~~~~~~~~~~~~~~~~~
*
* Minthub has THREE admin roles:
* - DEFAULT_ADMIN_ROLE:
* - Can grant and revoke membership of other roles.
* - There can only be one address with the DEFAULT_ADMIN_ROLE at a time, with transfer of
* the DEFAULT_ADMIN_ROLE governed by a two step process and delay period. For more details please
* see: https://docs.openzeppelin.com/contracts/5.x/api/access#AccessControlDefaultAdminRules
* - PLATFORM_ADMIN_ROLE:
* - Can set the minimum balance.
* - Can set the NFT treasury address.
* - Can set the platform fee treasury address.
* - Can set the platform fee percentage.
* - Can pause and unpause draws and deposits.
* - Can cancel a raffle.
* - Can override a temporary withdrawal/deposit pause to OFF.
* - Can update the details of a raffle.
* - Can rescue ERC721 and ERC20 tokens from the contract.
* - Can return all deposits to users.
* - RAFFLE_ADMIN_ROLE:
* - Can create raffles.
* - BATCH_OPERATOR_ROLE:
* - Can call the batch operations that conduct the draw process.
*
* Raffle Flow
* ~~~~~~~~~~~
*
* 1. Admin creates a raffle
* [Raffle is created and set to the *PreDraw* status]
* 2. The raffle is scheduled to be drawn at a future timestamp.
* 3. Before the draw timestamp users are free to deposit and withdraw funds.
* 4. After the draw timestamp a batch operator address can commence the drawing process which is as follows:
* 5. startDraw: Request randomness from Pyth Entropy. In addition to requesting entropy this call
* places a temporary pause on withdrawals and deposits.
* - Can only be called after the closedTimestamp
* -> Progresses the raffle to the *DrawStarted* status
* 6. Entropy callback receives the random number. Entropy is stored in the raffle object and
* an event is emitted.
* -> Progresses the raffle to the *RandomnessReceived* status
* 7. enumerateTicketsBatch: Tickets are enumerated.
* - Can only be called at *RandomnessReceived*
* -> Progresses the raffle to the *TicketsEnumerated* status]
* 8. drawWinningTickets: Prizes are drawn.
* - Can only be called at *TicketsEnumerated*
* -> Progresses the raffle to the *DrawComplete* status]
* 9. distributeAllPrizesForRaffleBatch: Prizes are distributed.
* - Can only be called at *DrawComplete*]
* 10. The raffle is complete.
*
* @dev AccessControl: This contract imports AccessControlDefaultAdminRules directly from
* OpenZeppelin. Within this contract we implement the functionality of OpenZeppelin's
* AccessControlEnumerable directly, allowing the clean inheritance of Enumeration -> default rule ->
* base contract class, therefore avoiding the need to override base class methods.
*/
import {
EnumerableMap
} from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
import {
AccessControlDefaultAdminRulesEnumerable
} from "./AccessControlDefaultAdminRulesEnumerable.sol";
import {
EnumerableSet
} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {
ReentrancyGuard
} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { IEntropyV2 } from "@pythnetwork/entropy-sdk-solidity/IEntropyV2.sol";
import {
IEntropyConsumer
} from "@pythnetwork/entropy-sdk-solidity/IEntropyConsumer.sol";
import { StrideFeistelSetGenerator } from "./StrideFeistelSetGenerator.sol";
import { TimeLock } from "./TimeLock.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {
IERC721Receiver
} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import { UnstoppableTransfer } from "./UnstoppableTransfer.sol";
contract Minthub is
AccessControlDefaultAdminRulesEnumerable,
ReentrancyGuard,
IEntropyConsumer,
TimeLock,
UnstoppableTransfer
{
using EnumerableMap for EnumerableMap.AddressToUintMap;
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.UintSet;
// RaffleStatus enum, used to track the status of a raffle. The stages of a
// draw occur in a strict order, controlled by the status of the raffle.
enum RaffleStatus {
PreDraw,
DrawStarted,
RandomnessReceived,
TicketsEnumerated,
DrawComplete,
Cancelled
}
// ParticipationListType enum, used to track the type of participation list for a user.
// All: User will be entered into all raffles.
// Include: User will be entered into the specified raffles.
// Exclude: User will NOT be entered into the specified raffles.
// None: User will not be entered into ANY raffles.
enum ParticipationListType {
All,
Include,
Exclude,
None
}
// PrizeStatus enum, used to track the status of a prize.
// NotPaid: The prize has not been paid for. Payment must be made before the prize can be distributed.
// ReadyForDistribution: The prize is paid for and ready to be distributed.
// Distributed: The prize has been distributed.
enum PrizeStatus {
NotPaid,
ReadyForDistribution,
Distributed
}
// Maximum platform users. This constant defines the maximum number of simultaneous users
// can make use of this platform. This limit allows us to use a fixed-length array for
// ticketRanges, reducing storage costs.
uint256 private constant MAX_PLATFORM_USERS = 10_000_000;
// Maximum number of prizes that can be awarded in a single raffle. This constant defines
// the maximum number of prizes that can be awarded in a single raffle. This limit allows
// us to use a fixed-length array for winningTickets, reducing storage costs.
uint256 private constant MAX_PRIZES = 10_000;
// MAX_LOCK_SECONDS. This determines the maximum number of seconds that deposits and
// withdrawals can be locked for.
// As a safety feature the lock on withdrawals and deposits can only apply for a maximum time period.
// After this time has elapsed the lock is automatically lifted. This means that there is
// a triple fallback for the lock being lifted:
// 1. At the completion of the draw process the lock is lifted.
// 2. If for any reason the draw process is not completed within the MAX_LOCK_SECONDS the lock
// is lifted automatically.
// 3. Members of the PLATFORM_ADMIN_ROLE can manually lift the lock.
uint256 private constant MAX_LOCK_SECONDS = 10800; // Three hours
// MIN_LOCK_GAP_SECONDS. This is the minimum number of seconds that must pass between
// the last unlock timestamp and a new lock being applied.
uint256 private constant MIN_LOCK_GAP_SECONDS = 120; // Two minutes
// Platform admin role. See header comment for details of role authorities.
// keccak256("PLATFORM_ADMIN_ROLE");
bytes32 private constant PLATFORM_ADMIN_ROLE =
0x5246556c0ab216b5b325ad7c539bfbd1a07c768773cdf810fecd3c3375c27407;
// Raffle admin role. See header comment for details of role authorities.
// keccak256("RAFFLE_ADMIN_ROLE");
bytes32 private constant RAFFLE_ADMIN_ROLE =
0x5b4b8088d200445bfb44acef5ae68c0906d30def45a3f841c2e4c6f0a2b1e914;
// Batch operator role. See header comment for details of role authorities.
// keccak256("BATCH_OPERATOR_ROLE");
bytes32 private constant BATCH_OPERATOR_ROLE =
0x52e466598c5e4408cbbaf840ea4dbd39f3e0aa1ba17a69b571858fba5698291f;
// Maximum platform fee percentage (10% = 1000 basis points)
uint256 private constant MAX_PLATFORM_FEE_PERCENTAGE = 1000;
// Basis points denominator
uint256 private constant BASIS_POINTS_DENOMINATOR = 10000;
// minOpenPeriodSeconds. This is the minimum number of seconds that a raffle must be open for,
// i.e. the time between it's creation and the start of the draw. This is a user protection
// feature intended to allow all existing depositors a minimum amount of time to consider a new raffle
// before being entered into it. It also allows users a minimum amount of time to
// deposit to the contract if they wish to have tickets in the raffle. This value is set in the
// constructor and is immutable (cannot be updated after construction).
// This validation applies on creation and update of a raffle. This means that if the platform admin
// updates any part of a raffle the new closed timestamp must be at least minOpenPeriodSeconds in the future,
// giving depositors a minimum amount of time to consider the updated raffle before the draw starts.
uint256 private immutable minOpenPeriodSeconds;
// Pyth Entropy for randomness
IEntropyV2 private immutable entropy;
// Address of the set generator
StrideFeistelSetGenerator private immutable setGenerator;
// The minimumBalance specifies the lowest balance a user can hold on this contract.
// Deposits will not be allowed where the resulting balance would be below this minimum.
// Likewise users cannot make a withdrawal such that their balance would be below this
// amount AND non-0. In other words, users can withdraw all of their balance but they
// cannot leave a small amount that is below the minimumBalance. For example, if the
// minimumBalance is 50 and the user has a balance of 100 they can withdraw 50 (as that
// leaves a balance above 50). They can also withdraw 100 as that takes their balance to 0
// and removes them from the user enumeration. But they cannot withdraw 51, as that would
// leave them with a balance of 49 and that is below the minimum of 50. We impose this to
// prevent the need to attempt ticket enumeration on a large number of users with balances
// below the threshold to get a ticket.
uint256 public minimumBalance;
// Global platform fee percentage for withdrawals (in basis points). This is the fee for using
// the platform. Users taking part in raffles pay this fee on any balance withdrawn from the contract.
// This fee covers the cost of operating the platform and is paid to the platform fee treasury.
uint256 public platformFeePercentage;
// Treasury address that holds the NFTs to be distributed. This address MUST have setApprovalForAll
// called on the NFT contract for the Minthub contract to be able to transfer NFTs from the treasury.
address public nftTreasury;
// Platform fee treasury address for collecting platform fees
address public platformFeeTreasury;
// Draws Paused. Boolean flag used to prevent the startDraw function from being called if it is true.
bool private drawsPaused;
// Deposits Paused. Boolean flag used to prevent new deposits from being made if it is true.
bool private depositsPaused;
// Accumulated platform fees. This is the total amount of platform fees that have been accumulated
// and are eligible for withdrawal to the platform fee treasury.
uint256 public accumulatedPlatformFees;
// Timestamp of when deposit balances last changed. Used to ensure that no balance changes have
// occurred during the enumerate tickets and draw winning tickets processes.
uint256 private depositBalancesLastChangedTimestamp;
// Mapping from user address to balances. This is used to store the balances of the users.
// We use an EnumerableMap as it allows us to easily iterate over the users and their balances
// as part of the winner selection process.
EnumerableMap.AddressToUintMap private userBalances;
// Mapping from user address to array of prize indexes for each raffle
mapping(address user => mapping(uint256 raffleId => uint256[] prizeIndexes))
private userPrizes;
// Mapping of raffles that a user has prizes in:
mapping(address user => uint256[] raffleIds) private winningRafflesForUser;
// Mapping of participation lists for each user
mapping(address user => Participation participation)
private participationForUser;
// Mapping from sequence number to raffle ID for callback tracking
mapping(uint64 => uint256) private sequenceToRaffle;
// Array of raffles.
Raffle[] private raffles;
// RaffleConfig struct, used to store the configuration of a raffle.
struct RaffleConfig {
// ticketPrice is the amount that will be deducted from a winner's balance if they win a prize.
uint256 ticketPrice;
// The raffle can be drawn by after the closedTimestamp.
uint256 closedTimestamp;
// nftAddress is the contract address of the NFT collection that will be distributed as prizes.
address nftAddress;
// tokenIds is the array of token IDs that will be distributed as prizes. We hold this as a fixed
// size array to achieve packing of the token IDs (16 per slot). This means the MAXIMUM tokenId we
// can support is a max uint16 i.e. 65535.
uint16[MAX_PRIZES] tokenIds;
// tokenIdsCount is the number of token IDs that will be distributed as prizes.
uint256 tokenIdsCount;
// The projectTreasury address will receive the total ticket price minus the platform fee.
address projectTreasury;
// raffleFeePercentage is the percentage of the ticket price that will be deducted as a platform fee.
uint256 raffleFeePercentage;
}
// Entropy struct, used to store the entropy for a raffle. Entropy is sourced from Pyth Entropy.
struct Entropy {
// sequenceNumber is the sequence number of the entropy request.
uint64 sequenceNumber;
// randomNumber is the random number received from the entropy request.
bytes32 randomNumber;
}
// UserTickets struct, used to store user ticket ranges for a raffle. We use a struct as that allows
// us to store the ticket ranges in a fixed-length array to take advantage of storage packing of the uint32s.
struct UserTickets {
// ranges holds the end range of tickets for each user, allowing users to hold multiple tickets
// without incurring additional storage costs.
uint32[MAX_PLATFORM_USERS] ranges;
// enumerationIndex is the index of the next user to have their tickets enumerated. This allows
// us to batch the enumeration process. When the enumerationIndex is equal to the length of the
// user enumeration, tickets enumeration is complete.
uint256 enumerationIndex;
// total is the number of tickets that have been enumerated from user balances for the raffle.
uint256 total;
// length is the number of users that have tickets for the raffle. This is the length of used
// uint32s in the ranges array. This allows us to iterate the ranges array which is a fixed width
// to take advantage of storage packing of the uint32s.
uint256 length;
}
// PrizeItem holds the winner of a prize and whether it has been distributed.
// An array of these structs is used to store the prizes awarded in a raffle.
struct PrizeItem {
address winner;
uint32 ticketNumber;
uint32 prizeIndex;
PrizeStatus prizeStatus;
}
// RafflePrizes struct, used to store the array of prize items for a raffle alongside processing
// and distribution counts.
struct RafflePrizes {
// Array of winner addresses with transferred status
PrizeItem[] items;
// autoProcessedCount is the total number of prizes that have been attempted to be distributed.
uint256 autoProcessedCount;
// distributedCount is the total number of prizes that have been distributed.
uint256 distributedCount;
}
// The Raffle struct holds the details of a raffle.
// An array of these structs is used to store the raffles in the contract.
struct Raffle {
RaffleConfig config;
Entropy entropy;
UserTickets userTickets;
RafflePrizes prizes;
RaffleStatus status;
// stableBalancesRequiredFromTimestamp is the timestamp when ticket enumeration started.
uint256 stableBalancesRequiredFromTimestamp;
}
// UserNFTsReport struct, this is never stored on-chain but used to structure user
// NFT data on a view method call.
struct UserNFTsReport {
// Raffle ID
uint256 raffleId;
// Contract address of the NFT collection
address nftAddress;
// Array of NFT token IDs
uint256[] tokenIds;
// Closed timestamp of the raffle
uint256 raffleClosedTimestamp;
}
// Concise raffle struct containing all raffle data except fixed-length arrays. This is never
// stored on-chain but used to structure raffle data on a view method call.
struct ConciseRaffleReport {
uint256 raffleId;
// RaffleConfig data (excluding tokenIds array)
uint256 ticketPrice;
uint256 closedTimestamp;
address nftAddress;
uint256 tokenIdsCount;
address projectTreasury;
uint256 raffleFeePercentage;
// Entropy data
uint64 sequenceNumber;
bytes32 randomNumber;
// UserTickets data (excluding ranges array)
uint256 enumerationIndex;
uint256 totalTickets;
// Prizes data (excluding tickets array)
uint256 prizesCount;
uint256 distributedCount;
// RaffleStatus data
RaffleStatus status;
}
// Participation struct, used to store the participation list for a user.
struct Participation {
ParticipationListType listType;
EnumerableSet.UintSet raffleIds;
}
event Deposit(
address indexed caller,
address indexed user,
uint256 deposit,
uint256 balance,
bool newUser
);
event Withdrawal(address indexed user, uint256 amount, uint256 newBalance);
event RaffleUpdated(
uint256 indexed raffleId,
bool newRaffle,
uint256 ticketPrice,
uint256 closedTimestamp,
address nftAddress,
uint256 tokenIdsCount,
address projectTreasury,
uint256 raffleFeePercentage
);
event TicketsEnumerated(
uint256 indexed raffleId,
uint256 startUserIndex,
uint256 endUserIndex,
uint256 totalTickets,
bool ticketEnumerationComplete
);
event PrizeAwarded(
uint256 indexed raffleId,
address indexed winner,
uint256 prizeIndex,
uint256 ticketNumber
);
event WinningTicketsDrawnBatch(
uint256 indexed raffleId,
uint256 startIndex,
uint256 count,
uint32[] winningTickets
);
event PrizesDistributed(
uint256 indexed raffleId,
uint256 startIndex,
uint256 count,
uint256 distributionCount
);
event NftTreasurySet(address oldNftTreasury, address newNftTreasury);
event PlatformFeeTreasurySet(
address oldPlatformFeeTreasury,
address newPlatformFeeTreasury
);
event PlatformFeePercentageSet(
uint256 oldPlatformFeePercentage,
uint256 newPlatformFeePercentage
);
event UserPrizesDistributedForRaffle(
uint256 indexed raffleId,
address indexed user,
uint256 distributedCount
);
event MinimumBalanceSet(uint256 oldMinimumBalance, uint256 newMinimumBalance);
event PrizeStatusUpdated(
uint256 indexed raffleId,
uint256 indexed prizeIndex,
address indexed winner,
uint256 ticketNumber,
PrizeStatus newStatus
);
event DrawsPausedSet(bool paused);
event DepositsPausedSet(bool paused);
event TicketRangeEnumeratedForUser(
uint256 indexed raffleId,
address indexed user,
uint256 index,
uint256 startTicketNumber,
uint256 numberOfTickets
);
event AutoDistributionComplete(uint256 indexed raffleId);
event NoWinner(uint256 indexed raffleId, uint256 indexed ticketIndex);
event LogRaffleStatus(
uint256 indexed raffleId,
RaffleStatus indexed newStatus
);
event ManualUnlock();
event ERC721ManualTransfer(uint256[] tokenIds);
event ParticipationUpdated(
address indexed user,
ParticipationListType listType,
uint256[] raffleIds
);
modifier onlyValidRaffleId(uint256 raffleId_) {
if (raffleId_ >= raffles.length) {
revert("Invalid raffle ID");
}
_;
}
modifier onlyWhenDrawsUnpaused() {
if (drawsPaused) {
revert("Draws are paused");
}
_;
}
modifier onlyWhenDepositsUnpaused() {
if (depositsPaused) {
revert("Deposits are paused");
}
_;
}
modifier onlyInPreDrawStatus(uint256 raffleId_) {
if (raffles[raffleId_].status != RaffleStatus.PreDraw) {
revert("Raffle is not in pre draw status");
}
_;
}
modifier onlyBatchOperatorWhenLocked() {
// Distributions can only be made by BATCH_OPERATOR_ROLE during
// locked periods
if (_isLocked() && !hasRole(BATCH_OPERATOR_ROLE, msg.sender)) {
revert("Locked");
}
_;
}
modifier onlyWhenStableBalances(uint256 raffleId_) {
Raffle storage raffle = raffles[raffleId_];
// Record the stableBalancesRequiredFromTimestamp if it is currently zero.
// We use this to check if any balance changes have occurred during the enumerate tickets
// and draw winning tickets processes. Balances are locked during the draw process using a
// timelock. The timelock auto-expires in order to protect a user's ability to withdraw their
// funds. But this does open up a small possibility of the draw process extending into a period
// of time when balances can change. By recording the timestamp when we first call this for a raffle
// we can check if the depositBalancesLastChangedTimestamp is greater than the
// stableBalancesRequiredFromTimestamp.
// If this is the case we revert as the draw process is no longer safe to proceed.
if (raffle.stableBalancesRequiredFromTimestamp == 0) {
raffle.stableBalancesRequiredFromTimestamp = block.timestamp;
} else {
if (
depositBalancesLastChangedTimestamp >=
raffle.stableBalancesRequiredFromTimestamp
) {
revert("Balances have changed during draw process");
}
}
_;
}
/**
* @param initialDelay_ The initial delay for the timelock.
* @param initialDefaultAdmin_ The initial default admin address.
* @param platformAdmins_ An array of addresses to be granted the PLATFORM_ADMIN role.
* @param batchOperators_ An array of addresses to be granted the BATCH_OPERATOR role.
* @param raffleAdmins_ An array of addresses to be granted the RAFFLE_ADMIN role.
* @param minimumBalance_ The minimum balance a user can hold on the contract.
* @param nftTreasury_ The NFT treasury address that holds NFTs to be distributed.
* @param platformFeeTreasury_ The platform fee treasury address for collecting platform fees.
* @param platformFeePercentage_ The global platform fee percentage for withdrawals (in basis points).
* @param entropyAddress_ The Pyth Entropy contract address.
* @param minOpenPeriodSeconds_ The minimum number of seconds that a raffle must be open for.
* @param wrappedNativeToken_ The wrapped native token contract address.
*/
constructor(
uint48 initialDelay_,
address initialDefaultAdmin_,
address[] memory platformAdmins_,
address[] memory raffleAdmins_,
address[] memory batchOperators_,
uint256 minimumBalance_,
address nftTreasury_,
address platformFeeTreasury_,
uint256 platformFeePercentage_,
address entropyAddress_,
uint256 minOpenPeriodSeconds_,
address wrappedNativeToken_
)
AccessControlDefaultAdminRulesEnumerable(
initialDelay_,
initialDefaultAdmin_
)
TimeLock(MAX_LOCK_SECONDS, MIN_LOCK_GAP_SECONDS)
UnstoppableTransfer(wrappedNativeToken_)
{
// Grant the PLATFORM_ADMIN_ROLE to all specified admins (if any):
for (uint256 i = 0; i < platformAdmins_.length; i++) {
_grantRole(PLATFORM_ADMIN_ROLE, platformAdmins_[i]);
}
// Grant the RAFFLE_ADMIN_ROLE to all specified admins (if any):
for (uint256 i = 0; i < raffleAdmins_.length; i++) {
_grantRole(RAFFLE_ADMIN_ROLE, raffleAdmins_[i]);
}
// The RAFFLE_ADMIN_ROLE is controlled by the PLATFORM_ADMIN_ROLE. Only
// addresses with the PLATFORM_ADMIN_ROLE can create new RAFFLE_ADMIN_ROLE
// addresses.
_setRoleAdmin(RAFFLE_ADMIN_ROLE, PLATFORM_ADMIN_ROLE);
// Grant the BATCH_OPERATOR_ROLE to all specified batch operators (if any):
for (uint256 i = 0; i < batchOperators_.length; i++) {
_grantRole(BATCH_OPERATOR_ROLE, batchOperators_[i]);
}
// The BATCH_OPERATOR_ROLE is controlled by the PLATFORM_ADMIN_ROLE. Only
// addresses with the PLATFORM_ADMIN_ROLE can create new BATCH_OPERATOR_ROLE
// addresses.
_setRoleAdmin(BATCH_OPERATOR_ROLE, PLATFORM_ADMIN_ROLE);
if (platformFeePercentage_ > MAX_PLATFORM_FEE_PERCENTAGE) {
revert("Platform fee exceeds maximum");
}
if (entropyAddress_ == address(0)) {
revert("Entropy address cannot be zero address");
}
if (platformFeeTreasury_ == address(0)) {
revert("Platform fee treasury cannot be zero address");
}
if (nftTreasury_ == address(0)) {
// If the NFT treasury hasn't been set we default to this contract being the
// NFT treasury.
nftTreasury = address(this);
} else {
nftTreasury = nftTreasury_;
}
minimumBalance = minimumBalance_;
platformFeeTreasury = platformFeeTreasury_;
platformFeePercentage = platformFeePercentage_;
entropy = IEntropyV2(entropyAddress_);
minOpenPeriodSeconds = minOpenPeriodSeconds_;
// Create the setGenerator set generator
setGenerator = new StrideFeistelSetGenerator();
}
/**
* ======================
* === Getter Methods ===
* ======================
*/
/**
* @notice This contract implements a large number of view methods with the aim of making the outcome of
* each raffle easy to query and understand.
*/
/**
* @dev depositOf Returns the balance of a user. If there is no recorded balance for this
* user this function returns 0, it does not revert.
* @param user_ Get the balance for this address.
* @return balance_ Return the balance of the user.
*/
function depositOf(address user_) public view returns (uint256 balance_) {
(, uint256 balance) = userBalances.tryGet(user_);
return (balance);
}
/**
* @dev userCount
*/
function userCount() public view returns (uint256 count_) {
return userBalances.length();
}
/**
* @dev raffleCount
*/
function raffleCount() public view returns (uint256 count_) {
return raffles.length;
}
function _validateAndCalculateIndexLength(
uint256 startIndex_,
uint256 count_,
uint256 total_
) internal pure returns (uint256 indexLength_) {
// The start index must be less than the total. This is because we are using
// zero indexed arrays and the total is the length of the array. An index the same
// as the total is out of bounds (e.g. an array of length 3 has indices 0, 1, 2).
if (startIndex_ >= total_) {
revert("Start index must be less than the total");
}
if (count_ == 0 || startIndex_ + count_ > total_) {
indexLength_ = total_ - startIndex_;
} else {
indexLength_ = count_;
}
return (indexLength_);
}
/**
* @dev getPrizeDetailsForRaffle: Get prize details for a raffle (in batches)
* @param raffleId_ The raffle ID
* @param startIndex_ Starting index in the winners array
* @param count_ Number of tickets to return (0 = return all remaining)
* @return prizes_ Array of prize objects
*/
function getPrizeDetailsForRaffle(
uint256 raffleId_,
uint256 startIndex_,
uint256 count_
)
public
view
onlyValidRaffleId(raffleId_)
returns (PrizeItem[] memory prizes_)
{
Raffle storage raffle = raffles[raffleId_];
uint256 totalPrizes = raffle.prizes.items.length;
// If there are no prize details, return an empty array
if (totalPrizes == 0) {
return prizes_;
}
// Validate start index and calculate index length
uint256 indexLength = _validateAndCalculateIndexLength(
startIndex_,
count_,
totalPrizes
);
// Create memory array with only the needed size
prizes_ = new PrizeItem[](indexLength);
// Copy from storage to memory (only the relevant portion)
for (uint256 i = 0; i < indexLength; i++) {
prizes_[i] = raffle.prizes.items[startIndex_ + i];
}
return prizes_;
}
/**
* @dev getTicketRangesForRaffle: Get ticket ranges for a raffle (in batches)
* @param raffleId_ The raffle ID
* @param startIndex_ Starting index in the user enumeration
* @param count_ Number of users to return (0 = return all remaining)
* @return ticketRanges_ Array of TicketRangeReport structs
*/
function getTicketRangesForRaffle(
uint256 raffleId_,
uint256 startIndex_,
uint256 count_
)
public
view
onlyValidRaffleId(raffleId_)
returns (uint32[] memory ticketRanges_)
{
Raffle storage raffle = raffles[raffleId_];
uint256 totalTicketRanges = raffle.userTickets.length;
// If there are no ticket ranges, return an empty array
if (totalTicketRanges == 0) {
return ticketRanges_;
}
// Validate start index and calculate index length
uint256 indexLength = _validateAndCalculateIndexLength(
startIndex_,
count_,
totalTicketRanges
);
// Create memory array with only the needed size
ticketRanges_ = new uint32[](indexLength);
// Copy from storage to memory (only the relevant portion)
for (uint256 i = 0; i < indexLength; i++) {
ticketRanges_[i] = raffle.userTickets.ranges[startIndex_ + i];
}
return ticketRanges_;
}
/**
* @dev getTokenIdsForRaffle: Get token IDs for a raffle's prizes (in batches)
* @param raffleId_ The raffle ID
* @param startIndex_ Starting index in the tokenIds array
* @param count_ Number of token IDs to return (0 = return all remaining)
* @return tokenIds_ Array of token IDs that will be distributed as prizes
*/
function getTokenIdsForRaffle(
uint256 raffleId_,
uint256 startIndex_,
uint256 count_
)
public
view
onlyValidRaffleId(raffleId_)
returns (uint16[] memory tokenIds_)
{
Raffle storage raffle = raffles[raffleId_];
uint256 totalTokenIds = raffle.config.tokenIdsCount;
// If there are no token IDs, return an empty array
if (totalTokenIds == 0) {
return tokenIds_;
}
// Validate start index and calculate index length
uint256 indexLength = _validateAndCalculateIndexLength(
startIndex_,
count_,
totalTokenIds
);
tokenIds_ = new uint16[](indexLength);
// Copy from storage to memory (only the relevant portion)
for (uint256 i = 0; i < indexLength; i++) {
tokenIds_[i] = raffle.config.tokenIds[startIndex_ + i];
}
return tokenIds_;
}
/**
* @dev getConciseRaffles: Get concise raffle data for a range of raffles
* (excluding fixed-length arrays)
* @param startIndex_ Starting index in the raffles array
* @param count_ Number of raffles to return (0 = return all remaining)
* @return conciseRaffles_ Array of ConciseRaffleReport structs
*/
function getConciseRaffles(
uint256 startIndex_,
uint256 count_
) public view returns (ConciseRaffleReport[] memory conciseRaffles_) {
uint256 totalRaffles = raffles.length;
uint256 indexLength = _validateAndCalculateIndexLength(
startIndex_,
count_,
totalRaffles
);
conciseRaffles_ = new ConciseRaffleReport[](indexLength);
for (uint256 i = 0; i < indexLength; i++) {
Raffle storage raffle = raffles[startIndex_ + i];
conciseRaffles_[i] = ConciseRaffleReport({
raffleId: startIndex_ + i,
// RaffleConfig data
ticketPrice: raffle.config.ticketPrice,
closedTimestamp: raffle.config.closedTimestamp,
nftAddress: raffle.config.nftAddress,
tokenIdsCount: raffle.config.tokenIdsCount,
projectTreasury: raffle.config.projectTreasury,
raffleFeePercentage: raffle.config.raffleFeePercentage,
// Entropy data
sequenceNumber: raffle.entropy.sequenceNumber,
randomNumber: raffle.entropy.randomNumber,
// UserTickets data (excluding ranges array)
enumerationIndex: raffle.userTickets.enumerationIndex,
totalTickets: raffle.userTickets.total,
// Prize data (excluding prizeItems array)
prizesCount: raffle.prizes.items.length,
distributedCount: raffle.prizes.distributedCount,
// RaffleStatus data
status: raffle.status
});
}
return conciseRaffles_;
}
/**
* @dev getUserPrizes Returns all prizes for a user.
* @param user_ The user to get the prizes for.
* @param startIndex_ The start index of the raffles to return
* @param count_ The number of raffles to return. Pass 0 to return all raffles
* from the start index to the end of the raffles array.
* @return userPrizes_ The user's prizes
*/
function getUserPrizes(
address user_,
uint256 startIndex_,
uint256 count_
) external view returns (UserNFTsReport[] memory userPrizes_) {
uint256 totalRaffles = winningRafflesForUser[user_].length;
uint256 indexLength = _validateAndCalculateIndexLength(
startIndex_,
count_,
totalRaffles
);
userPrizes_ = new UserNFTsReport[](indexLength);
for (uint256 i = 0; i < indexLength; i++) {
uint256 raffleId = winningRafflesForUser[user_][startIndex_ + i];
Raffle storage raffle = raffles[raffleId];
uint256 count = userPrizes[user_][raffleId].length;
uint256[] memory tokenIds = new uint256[](count);
for (uint256 j = 0; j < count; j++) {
uint256 prizeIndex = userPrizes[user_][raffleId][j];
tokenIds[j] = raffle.config.tokenIds[prizeIndex];
}
userPrizes_[i] = UserNFTsReport({
raffleId: raffleId,
nftAddress: raffle.config.nftAddress,
tokenIds: tokenIds,
raffleClosedTimestamp: raffle.config.closedTimestamp
});
}
}
/**
* @dev getParticipationForUser Returns the participation list for a user.
* @param user_ The user to get the participation list for.
* @return listType_ The type of the participation list
* @return raffleIds_ The raffle IDs in the participation list
*/
function getParticipationForUser(
address user_
) external view returns (uint256 listType_, uint256[] memory raffleIds_) {
return (
uint256(participationForUser[user_].listType),
participationForUser[user_].raffleIds.values()
);
}
/**
* @dev getPauseAndLockStatus Returns the pause and lock status of the contract.
* @return drawsPaused_ The pause status of the draws
* @return depositsPaused_ The pause status of the deposits
* @return isLocked_ The lock status of the contract
* @return unlockAtTimestamp_ The timestamp when the lock will be lifted
*/
function getPauseAndLockStatus()
external
view
returns (
bool drawsPaused_,
bool depositsPaused_,
bool isLocked_,
uint256 unlockAtTimestamp_
)
{
return (drawsPaused, depositsPaused, _isLocked(), unlockAtTimestamp);
}
/**
* @dev allPrizesAreOwnedByTreasury Returns if all NFT prices are held in the treasury.
* @param raffleId_ The raffle ID
* @return allPrizesOwnedByTreasury_ True if all NFT prices are held in the treasury
*/
function allPrizesAreOwnedByTreasury(
uint256 raffleId_
) public view returns (bool allPrizesOwnedByTreasury_) {
Raffle storage raffle = raffles[raffleId_];
address nftContract = raffle.config.nftAddress;
for (uint256 i = 0; i < raffle.config.tokenIdsCount; i++) {
if (
IERC721(nftContract).ownerOf(raffle.config.tokenIds[i]) != nftTreasury
) {
return (false);
}
}
// We have reached here so all NFTs are owned by the treasury:
return (true);
}
/**
* =================================
* === Platform Admin Operations ===
* =================================
*/
/**
* @dev setMinimumBalance Sets the minimum balance that a user can have on the contract.
* @param minimumBalance_ The new minimum balance
*/
function setMinimumBalance(
uint256 minimumBalance_
) external onlyRole(PLATFORM_ADMIN_ROLE) {
uint256 oldMinimumBalance = minimumBalance;
minimumBalance = minimumBalance_;
emit MinimumBalanceSet(oldMinimumBalance, minimumBalance_);
}
/**
* @dev setNftTreasury Sets the NFT treasury address that holds NFTs to be distributed
* @param nftTreasury_ The new NFT treasury address
*/
function setNftTreasury(
address nftTreasury_
) external onlyRole(PLATFORM_ADMIN_ROLE) {
address oldNftTreasury = nftTreasury;
if (nftTreasury_ == address(0)) {
revert("NFT treasury cannot be zero address");
}
nftTreasury = nftTreasury_;
emit NftTreasurySet(oldNftTreasury, nftTreasury_);
}
/**
* @dev setPlatformFeeTreasury Sets the platform fee treasury address
* @param platformFeeTreasury_ The new platform fee treasury address
*/
function setPlatformFeeTreasury(
address platformFeeTreasury_
) external onlyRole(PLATFORM_ADMIN_ROLE) {
address oldPlatformFeeTreasury = platformFeeTreasury;
if (platformFeeTreasury_ == address(0)) {
revert("Platform fee treasury cannot be zero address");
}
platformFeeTreasury = platformFeeTreasury_;
emit PlatformFeeTreasurySet(oldPlatformFeeTreasury, platformFeeTreasury_);
}
/**
* @dev setPlatformFeePercentage Sets the global platform fee percentage for withdrawals
* @param platformFeePercentage_ The new platform fee percentage (in basis points)
*/
function setPlatformFeePercentage(
uint256 platformFeePercentage_
) external onlyRole(PLATFORM_ADMIN_ROLE) {
uint256 oldPlatformFeePercentage = platformFeePercentage;
if (platformFeePercentage_ > MAX_PLATFORM_FEE_PERCENTAGE) {
revert("Fee exceeds maximum");
}
platformFeePercentage = platformFeePercentage_;
emit PlatformFeePercentageSet(
oldPlatformFeePercentage,
platformFeePercentage_
);
}
/**
* @dev pauseDraws Pauses/unpauses the draws for all raffles
*/
function setDrawsPausedStatus(
bool paused_
) external onlyRole(PLATFORM_ADMIN_ROLE) {
if (drawsPaused == paused_) {
revert("Draws paused status is already set to the desired value");
}
drawsPaused = paused_;
emit DrawsPausedSet(paused_);
}
/**
* @dev pauseDeposits Pauses/unpauses the deposits for all raffles
*/
function setDepositsPausedStatus(
bool paused_
) external onlyRole(PLATFORM_ADMIN_ROLE) {
if (depositsPaused == paused_) {
revert("Deposits paused status is already set to the desired value");
}
depositsPaused = paused_;
emit DepositsPausedSet(paused_);
}
/**
* @dev cancelRaffle Sets the raffle close time to the infinite future and sets the status to cancelled
* @param raffleId_ The raffle ID
*/
function cancelRaffle(
uint256 raffleId_
) external onlyRole(PLATFORM_ADMIN_ROLE) onlyValidRaffleId(raffleId_) {
// A raffle cannot be cancelled if it has completed:
if (raffles[raffleId_].status == RaffleStatus.DrawComplete) {
revert("Cannot cancel a completed raffle");
}
raffles[raffleId_].config.closedTimestamp = type(uint256).max;
raffles[raffleId_].status = RaffleStatus.Cancelled;
emit LogRaffleStatus(raffleId_, RaffleStatus.Cancelled);
}
/**
* @dev unlock Allows the platform admin to manually unlock deposits and withdrawals
*/
function unlock() external onlyRole(PLATFORM_ADMIN_ROLE) {
_unlock();
emit ManualUnlock();
}
/**
* @dev rescueERC721 Rescues ERC721 tokens from the contract
* @param tokenAddress_ The address of the token to rescue
* @param tokenIds_ The IDs of the token to rescue
* @param to_ The address to rescue the token to
*/
function rescueERC721(
address tokenAddress_,
uint256[] calldata tokenIds_,
address to_
) external onlyRole(PLATFORM_ADMIN_ROLE) {
for (uint256 i = 0; i < tokenIds_.length; i++) {
IERC721(tokenAddress_).safeTransferFrom(address(this), to_, tokenIds_[i]);
}
emit ERC721ManualTransfer(tokenIds_);
}
/**
* @dev rescueERC20 Rescues an ERC20 token from the contract
* @param tokenAddress_ The address of the token to rescue
* @param amount_ The amount of the token to rescue
* @param to_ The address to rescue the token to
*/
function rescueERC20(
address tokenAddress_,
uint256 amount_,
address to_
) external onlyRole(PLATFORM_ADMIN_ROLE) {
IERC20(tokenAddress_).transfer(to_, amount_);
}
/**
* @dev returnDepositsBelowMinimumBalance Returns all deposits that are below the minimum balance,
* callable in batch by the PLATFORM_ADMIN_ROLE.
* This function is important for the long-term health of the protocol. Small balances are considered
* in any enumeration and therefore increase the cost of the draw process while never being able to
* generate a winning ticket. This function should only be run when required and users should be informed
* in advance, their option being to deposit more funds to increase their balance above the minimum
* or accept that they will be refunded their deposits.
* @param startIndex_ The start index of the users to return the deposits for
* @param count_ The number of users to return the deposits for
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function returnDepositsBelowMinimumBalance(
uint256 startIndex_,
uint256 count_,
uint256 maxGasForNativeTokenTransfer_
) external onlyRole(PLATFORM_ADMIN_ROLE) {
uint256 indexLength = _validateAndCalculateIndexLength(
startIndex_,
count_,
userBalances.length()
);
// Iterate backwards to avoid shifting indices
for (uint256 i = indexLength; i > 0; i--) {
uint256 currentIndex = startIndex_ + i - 1;
(address user, uint256 balance) = userBalances.at(currentIndex);
if (balance >= minimumBalance) {
continue;
}
_withdraw(user, balance, maxGasForNativeTokenTransfer_);
}
}
/**
* @dev updateRaffle Updates details on an existing raffle. As this is altering an existing raffle
* at any stage of the process it can only be called by the PLATFORM_ADMIN_ROLE. This is a higher level
* of authority than the RAFFLE_ADMIN_ROLE, which can create raffles
* @param raffleId_ The id of the raffle being updated
* @param ticketPrice_ Cost per ticket in wei
* @param closedTimestamp_ Timestamp when the raffle will be closed
* @param nftAddress_ NFT contract address for prizes
* @param tokenIds_ Array of token IDs for prizes
* @param projectTreasury_ Project treasury address for fee distribution
* @param raffleFeePercentage_ Platform fee percentage in basis points (e.g., 250 = 2.5%)
* @param validatePrizeOwnership_ Whether to validate that all ERC721 tokens are held by the NFT treasury.
*/
function updateRaffle(
uint256 raffleId_,
uint256 ticketPrice_,
uint256 closedTimestamp_,
address nftAddress_,
uint16[] calldata tokenIds_,
address projectTreasury_,
uint256 raffleFeePercentage_,
bool validatePrizeOwnership_
)
external
onlyRole(PLATFORM_ADMIN_ROLE)
onlyValidRaffleId(raffleId_)
onlyInPreDrawStatus(raffleId_)
{
_saveRaffle(
raffleId_,
false,
ticketPrice_,
closedTimestamp_,
nftAddress_,
tokenIds_,
projectTreasury_,
raffleFeePercentage_,
validatePrizeOwnership_
);
}
/**
* ===============================
* === Raffle Admin Operations ===
* ===============================
*/
/**
* @dev createRaffle Creates a new raffle with specified ticket price and token IDs
* @param ticketPrice_ Cost per ticket
* @param closedTimestamp_ Timestamp when the raffle will be closed
* @param nftAddress_ NFT contract address for prizes
* @param tokenIds_ Array of token IDs for prizes
* @param projectTreasury_ Project treasury address for fee distribution
* @param raffleFeePercentage_ Raffle fee percentage in basis points (e.g., 250 = 2.5%)
* @param validatePrizeOwnership_ Whether to validate that all ERC721 tokens are held by the NFT treasury.
* @return raffleId_ The ID of the new raffle
*/
function createRaffle(
uint256 ticketPrice_,
uint256 closedTimestamp_,
address nftAddress_,
uint16[] calldata tokenIds_,
address projectTreasury_,
uint256 raffleFeePercentage_,
bool validatePrizeOwnership_
) external onlyRole(RAFFLE_ADMIN_ROLE) returns (uint256 raffleId_) {
raffleId_ = raffles.length;
raffles.push();
_saveRaffle(
raffleId_,
true,
ticketPrice_,
closedTimestamp_,
nftAddress_,
tokenIds_,
projectTreasury_,
raffleFeePercentage_,
validatePrizeOwnership_
);
}
/**
* @dev _saveRaffle Sets parameters on a raffle
* @param ticketPrice_ Cost per ticket in wei
* @param nftAddress_ NFT contract address for prizes
* @param tokenIds_ Array of token IDs for prizes
* @param projectTreasury_ Project treasury address for fee distribution
* @param raffleFeePercentage_ Platform fee percentage in basis points (e.g., 250 = 2.5%)
* @param validatePrizeOwnership_ Whether to validate that all ERC721 tokens are held by the NFT treasury.
*/
function _saveRaffle(
uint256 raffleId_,
bool newRaffle_,
uint256 ticketPrice_,
uint256 closedTimestamp_,
address nftAddress_,
uint16[] calldata tokenIds_,
address projectTreasury_,
uint256 raffleFeePercentage_,
bool validatePrizeOwnership_
) internal {
if (ticketPrice_ == 0) {
revert("Ticket price must be > 0");
}
if (nftAddress_ == address(0)) {
revert("NFT address cannot be zero");
}
if (tokenIds_.length == 0) {
revert("Must have prizes");
}
if (projectTreasury_ == address(0)) {
revert("Project treasury cannot be zero");
}
if (raffleFeePercentage_ > BASIS_POINTS_DENOMINATOR) {
revert("Fee is greater than 100%");
}
if (tokenIds_.length > MAX_PRIZES) {
revert("Token IDs exceeds max prizes");
}
if (closedTimestamp_ < block.timestamp + minOpenPeriodSeconds) {
revert("Raffle must be open for at least minimum period");
}
// If this contract is not the NFT treasury, then the NFT treasury must approve Minthub to transfer NFTs
if (
nftTreasury != address(this) &&
!IERC721(nftAddress_).isApprovedForAll(nftTreasury, address(this))
) {
revert("NFT Treasury must approve Minthub to transfer NFTs");
}
Raffle storage raffle = raffles[raffleId_];
raffle.config.ticketPrice = ticketPrice_;
raffle.config.closedTimestamp = closedTimestamp_;
raffle.config.nftAddress = nftAddress_;
for (uint256 i = 0; i < tokenIds_.length; i++) {
// Validate that all ERC721 tokens are held by the NFT treasury, if
// we are validating in this call. We provide the option to skip this validation
// in case there are a very large number of prizes to validate that would
// cause issues with block gas limits, OR the NFTs aren't available at the point of
// raffle creation.
if (
validatePrizeOwnership_ &&
IERC721(nftAddress_).ownerOf(tokenIds_[i]) != nftTreasury
) {
revert("NFT Treasury does not hold all ERC721 tokens");
}
raffle.config.tokenIds[i] = tokenIds_[i];
}
raffle.config.tokenIdsCount = tokenIds_.length;
raffle.config.projectTreasury = projectTreasury_;
raffle.config.raffleFeePercentage = raffleFeePercentage_;
emit RaffleUpdated(
raffleId_,
newRaffle_,
ticketPrice_,
closedTimestamp_,
nftAddress_,
tokenIds_.length,
projectTreasury_,
raffleFeePercentage_
);
emit LogRaffleStatus(raffleId_, raffle.status);
}
/**
* @dev withdrawPlatformFees Allows accumulated platform fees to be withdrawn to the platform fee treasury.
* This is not an admin only method: any address can call this function to withdraw the accumulated platform
* fees to the platform fee treasury. Setting of the platform fee treasury IS an admin only method. This
* method validates that the platform fee treasury is set (i.e. not address(0)) before withdrawing the fees.
*/
function withdrawPlatformFees(
uint256 maxGasForNativeTokenTransfer_
) external nonReentrant {
if (platformFeeTreasury == address(0)) {
revert("Platform fee treasury not set");
}
if (accumulatedPlatformFees == 0) {
revert("No platform fees to withdraw");
}
uint256 platformFees = accumulatedPlatformFees;
accumulatedPlatformFees = 0;
_unstoppableTransfer(
platformFeeTreasury,
platformFees,
maxGasForNativeTokenTransfer_
);
}
/**
* =======================
* === User Operations ===
* =======================
*/
/**
* @dev deposit Adds a deposit for the caller i.e. msg.sender. Calls the public method
* depositForUser passing in the msg.sender as the user address.
*/
function deposit() external payable {
_depositForUser(msg.sender, msg.value);
}
/**
* @dev depositForUsers Adds a deposit for specified users. Platform admin only.
*
* This method is provided for flexibility. For example, a promotion where the prize is a 100
* balance on Minthub. This allows the admins running the promotion to directly add the balance
* to Minthub, rather than sending funds to the user and requesting they deposit.
* @param users_ The users we are increasing the balance for.
* @param depositPerUser_ The amount to deposit for each user.
*/
function depositForUsers(
address[] calldata users_,
uint256 depositPerUser_
) external payable onlyRole(PLATFORM_ADMIN_ROLE) {
// Validate that the total value being deposited is equal to the sum of the deposits per user.
uint256 totalDeposit = depositPerUser_ * users_.length;
if (msg.value != totalDeposit) {
revert("Total deposit does not equal the sum of the deposits per user");
}
for (uint256 i = 0; i < users_.length; i++) {
_depositForUser(users_[i], depositPerUser_);
}
}
/**
* @dev _depositForUser Adds a deposit for a specified user. Centralised internal method.
*
* @param user_ The user we are increasing the balance for.
* @param depositAmount_ The amount to deposit for the user.
*/
function _depositForUser(
address user_,
uint256 depositAmount_
) internal onlyWhenUnlocked onlyWhenDepositsUnpaused {
if (user_ == address(0)) {
revert("User cannot be zero address");
}
if (depositAmount_ == 0) {
revert("Cannot deposit zero");
}
uint256 newBalance = depositOf(user_) + depositAmount_;
if (newBalance < minimumBalance) {
revert("Balance below minimum");
}
bool newKey = userBalances.set(user_, newBalance);
if (newKey && userBalances.length() > (MAX_PLATFORM_USERS)) {
revert("Max platform users reached");
}
depositBalancesLastChangedTimestamp = block.timestamp;
emit Deposit(msg.sender, user_, depositAmount_, newBalance, newKey);
}
/**
* @dev withdrawCurrentBalance Allows users to withdraw all of their balance from the contract.
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function withdrawCurrentBalance(
uint256 maxGasForNativeTokenTransfer_
) external {
withdraw(depositOf(msg.sender), maxGasForNativeTokenTransfer_);
}
/**
* @dev withdraw Allows users to withdraw from the contract. In this method the amount to be
* withdraw is specified.
* @param value_ The amount to withdraw.
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function withdraw(
uint256 value_,
uint256 maxGasForNativeTokenTransfer_
) public {
if (value_ == 0) {
revert("Cannot withdraw zero");
}
_withdraw(msg.sender, value_, maxGasForNativeTokenTransfer_);
}
/**
* @dev _withdraw Internal method to handle withdrawal of funds from the contract.
* @param value_ The amount to withdraw.
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function _withdraw(
address user_,
uint256 value_,
uint256 maxGasForNativeTokenTransfer_
) internal nonReentrant onlyWhenUnlocked {
uint256 priorBalance = depositOf(user_);
if (value_ > priorBalance) {
revert("Amount exceeds balance");
}
uint256 newBalance = priorBalance - value_;
// If this withdrawal takes the user balance to zero we
// remove that user from the enumeration altogether:
if (newBalance == 0) {
userBalances.remove(user_);
} else {
if (newBalance < minimumBalance) {
revert("Balance below minimum");
}
userBalances.set(user_, newBalance);
}
// Calculate platform fee:
uint256 platformFee = (value_ * platformFeePercentage) /
BASIS_POINTS_DENOMINATOR;
// Calculate user amount:
uint256 userAmount = value_ - platformFee;
// Add the fee amount to the accumulated platform fees:
accumulatedPlatformFees += platformFee;
// Transfer remaining amount to user
if (userAmount > 0) {
_unstoppableTransfer(user_, userAmount, maxGasForNativeTokenTransfer_);
}
depositBalancesLastChangedTimestamp = block.timestamp;
emit Withdrawal(user_, value_, newBalance);
}
/**
* @dev updateParticipationAndOptionallyDeposit Updates the participation list for the msg.sender.
* and optionally deposits funds into the contract.
* @param listType_ The type of the participation list
* @param raffleIds_ The raffle IDs in the participation list
*/
function updateParticipationAndOptionallyDeposit(
ParticipationListType listType_,
uint256[] calldata raffleIds_
) external payable onlyWhenUnlocked {
participationForUser[msg.sender].listType = listType_;
participationForUser[msg.sender].raffleIds.clear();
if (
listType_ != ParticipationListType.All &&
listType_ != ParticipationListType.None
) {
for (uint256 i = 0; i < raffleIds_.length; i++) {
participationForUser[msg.sender].raffleIds.add(raffleIds_[i]);
}
}
if (msg.value > 0) {
_depositForUser(msg.sender, msg.value);
}
emit ParticipationUpdated(msg.sender, listType_, raffleIds_);
}
/**
* ==============================
* === Entropy Implementation ===
* ==============================
*/
/**
* @dev getEntropy Returns the entropy contract address for callback validation
* @return entropy_ The entropy contract address
*/
function getEntropy() internal view override returns (address entropy_) {
return address(entropy);
}
/**
* @dev entropyCallback Called by Pyth Entropy when randomness is ready
* @param sequenceNumber_ The sequence number from the request
* @param randomNumber_ The final random number
*/
function entropyCallback(
uint64 sequenceNumber_,
address,
bytes32 randomNumber_
) internal override {
uint256 raffleId = sequenceToRaffle[sequenceNumber_];
require(raffleId < raffles.length, "Invalid ID");
Raffle storage raffle = raffles[raffleId];
if (raffle.status != RaffleStatus.DrawStarted) {
revert("Raffle is not in draw started status");
}
raffle.status = RaffleStatus.RandomnessReceived;
raffle.entropy.randomNumber = randomNumber_;
emit LogRaffleStatus(raffleId, RaffleStatus.RandomnessReceived);
}
/**
* ========================
* ==== Draw Operations ===
* ========================
*/
function includeUserInRaffle(
uint256 raffleId_,
address user_
) public view returns (bool) {
Participation storage participation = participationForUser[user_];
// No mapped value or explicitly list type of All means the user is included in all raffles.
if (participation.listType == ParticipationListType.All) {
return true;
}
// Explicitly type of None means the user is excluded from all raffles.
if (participation.listType == ParticipationListType.None) {
return false;
}
// Include list means the user is included ONLY in the raffles in the list.
if (participation.listType == ParticipationListType.Include) {
return participation.raffleIds.contains(raffleId_);
}
// Our last possible value: Exclude list means the user is excluded from raffles in the list.
return !participation.raffleIds.contains(raffleId_);
}
/**
* ------------------------------------------------
* (1) DRAW STEP ONE - Single call, NOT batchable
* ------------------------------------------------
* @dev startDraw Places a timelock on users balances and Requests randomness
* from Pyth Entropy. Can only process if all ERC721 tokens are held by the NFT treasury.
* @param raffleId_ The raffle ID
* @param validatePrizeOwnership_ Whether to validate that all ERC721 tokens are held by the NFT treasury.
*/
function startDraw(
uint256 raffleId_,
bool validatePrizeOwnership_
)
external
payable
onlyValidRaffleId(raffleId_)
onlyWhenDrawsUnpaused
onlyRole(BATCH_OPERATOR_ROLE)
{
Raffle storage raffle = raffles[raffleId_];
if (block.timestamp < raffle.config.closedTimestamp) {
revert("Raffle is not yet closed");
}
if (raffle.status != RaffleStatus.PreDraw) {
revert("Raffle is not in pre draw status");
}
// Validate that all ERC721 tokens are held by the NFT treasury, if
// we are validating in this call. We provide the option to skip this validation
// in case there are a very large number of prizes to validate that would
// cause issues with block gas limits.
if (validatePrizeOwnership_ && !allPrizesAreOwnedByTreasury(raffleId_)) {
revert("NFT Treasury does not hold all ERC721 tokens");
}
raffle.status = RaffleStatus.DrawStarted;
// This is the first stage of the draw process. Place the temporary lock on
// deposits and withdrawals:
_timelock();
// Check the entropy cost on chain and only send the amount required to cover the cost.
// Any excess will be refunded to the caller.
uint256 entropyCost = entropy.getFeeV2();
if (msg.value < entropyCost) {
revert("Insufficient value for entropy cost");
}
if (msg.value > entropyCost) {
(bool success, ) = msg.sender.call{ value: msg.value - entropyCost }("");
if (!success) {
revert("Failed to refund excess entropy cost");
}
}
uint64 sequenceNumber = entropy.requestV2{ value: entropyCost }();
if (sequenceNumber == 0) {
revert("Invalid sequence number");
}
raffle.entropy.sequenceNumber = sequenceNumber;
sequenceToRaffle[sequenceNumber] = raffleId_;
emit LogRaffleStatus(raffleId_, RaffleStatus.DrawStarted);
}
/**
* ------------------------------------------------
* (2) DRAW STEP TWO - Batchable
* ------------------------------------------------
* @dev enumerateTicketsBatch: Calculates ticket ranges for users in a raffle.
* This function can be called multiple times to process all users in batches
* @param raffleId_ The raffle ID
* @param batchSize_ Number of users to process in this batch
*/
function enumerateTicketsBatch(
uint256 raffleId_,
uint256 batchSize_
)
external
onlyValidRaffleId(raffleId_)
onlyWhenStableBalances(raffleId_)
onlyRole(BATCH_OPERATOR_ROLE)
{
Raffle storage raffle = raffles[raffleId_];
// This is the second stage of the draw process and can only be called
// when randomness has been received. Strictly speaking we don't need to
// wait for randomness to be received; it is enough to know that it has been
// requested and therefore we have a temporary lock on deposits and withdrawals
// and are safe to snapshot ticket ranges. However, to keep an entirely
// linear draw process we only enumerate tickets after randomness has been received.
if (raffle.status != RaffleStatus.RandomnessReceived) {
revert("Raffle is not in randomness received status");
}
// We need to check that the total possible tickets is within the bounds of
// our fixed uint types. We use a fixed length uint32 array for ranges, so
// the maximum possible tickets is 2^32 - 1 i.e. 4,294,967,295. If the total
// number of possible tickets is greater than this we can't enumerate the tickets.
// This fixed limitation allows us to pack items into storage, massively reducing
// storage costs. If we consider a typical raffle with a ticket price of $50
// we would need a total user balance of (4,294,967,295 * 50) = $214,748,364,750
// to exceed this check.
//
// 214 billion dollars seems like a reasonable ceiling for a single raffle, but be
// aware of this limitation if you are running a raffle with a very very low ticket
// price. Even $1 would require a balance of $4,294,967,295 to exceed this check,
// but if prizes are very very cheap you may encounter this practical limitation.
if (
((address(this).balance - accumulatedPlatformFees) /
raffle.config.ticketPrice) > (type(uint32).max - 1)
) {
revert(
"Total tickets exceeds maximum possible tickets: cannot enumerate at this ticket price."
);
}
uint256 totalUsers = userBalances.length();
uint256 startIndex = raffle.userTickets.enumerationIndex;
uint256 endIndex = startIndex + batchSize_;
// If our end index is greater than or equal to the total users we set the
// tickets fully enumerated flag and set the end index to the total users.
// (Yes, if endIndex already was equal to total users we are not changing
// the endIndex figure, but it's cleaner to handle both == and > in a single
// if statement as both set RaffleStatus.TicketsEnumerated.)
if (endIndex >= totalUsers) {
endIndex = totalUsers;
// Record the length of the used items in the userTickets array:
raffle.userTickets.length = totalUsers;
raffle.status = RaffleStatus.TicketsEnumerated;
emit LogRaffleStatus(raffleId_, RaffleStatus.TicketsEnumerated);
}
// Update the last enumerated index:
raffle.userTickets.enumerationIndex = endIndex;
uint256 currentTicketTotal = raffle.userTickets.total;
uint256 ticketPrice = raffle.config.ticketPrice;
// Process users in this batch
for (uint256 i = startIndex; i < endIndex; i++) {
(address user, uint256 balance) = userBalances.at(i);
uint256 priorTicketTotal = currentTicketTotal;
// Check if the user is included in the raffle
if (includeUserInRaffle(raffleId_, user)) {
// Calculate how many tickets this user can afford
currentTicketTotal += uint32(balance / ticketPrice);
}
// Direct assignment to fixed-length array (no push needed)
// Important: yes, we are potentially inserting 0 value ranges where the
// user either excludes this raffle or does not have a balance sufficient to buy a ticket.
// This is intentional as the ticket range indexes must match the userBalance indexes
// when we award prizes.
raffle.userTickets.ranges[i] = uint32(currentTicketTotal);
// Emit the ticket range for this user
emit TicketRangeEnumeratedForUser(
raffleId_,
user,
i,
priorTicketTotal,
currentTicketTotal - priorTicketTotal
);
}
// Update raffle state
raffle.userTickets.total = currentTicketTotal;
emit TicketsEnumerated(
raffleId_,
startIndex,
endIndex,
currentTicketTotal,
raffle.status == RaffleStatus.TicketsEnumerated
);
}
/**
* ------------------------------------------------
* (3) DRAW STEP THREE - Batchable
* ------------------------------------------------
* @dev drawWinningTicketsBatch. Winning tickets are drawn using a hybrid Stride + Feistel approach:
* coprime stride generates scattered inputs which are then permuted by a Feistel network.
* This creates a uniform, collision-free permutation.
* @param raffleId_ The raffle ID
* @param batchSize_ The number of winning tickets to generate in this batch
*/
function drawWinningTicketsBatch(
uint256 raffleId_,
uint256 batchSize_
)
external
onlyValidRaffleId(raffleId_)
onlyWhenStableBalances(raffleId_)
onlyRole(BATCH_OPERATOR_ROLE)
{
if (batchSize_ == 0) {
revert("BatchSize is 0");
}
Raffle storage raffle = raffles[raffleId_];
// Only allow winner generation while still in TicketsEnumerated
if (raffle.status != RaffleStatus.TicketsEnumerated) {
revert("Raffle not ready for winner generation");
}
uint256 ticketCount = raffle.userTickets.total;
if (ticketCount == 0) {
revert("No tickets");
}
uint256 prizeCount = raffle.config.tokenIdsCount;
// Total winners to generate: min(ticketCount, prizeCount)
uint256 totalToGenerate;
if (ticketCount < prizeCount) {
totalToGenerate = ticketCount;
} else {
totalToGenerate = prizeCount;
}
// How many prizes already generated (and stored)
uint256 start = raffle.prizes.items.length;
// Cap this batch to the remaining amount
uint256 remaining = totalToGenerate - start;
uint256 thisCount;
if (batchSize_ < remaining) {
thisCount = batchSize_;
} else {
thisCount = remaining;
}
// Generate the next slice from the permutation over [0 .. ticketCount-1]
uint32[] memory winners = setGenerator.generateSlice(
ticketCount - 1,
start,
thisCount,
raffle.entropy.randomNumber,
6
);
// Find winner and award prize
for (uint256 i = 0; i < thisCount; i++) {
// A ticket number of type(uint32).max can only be returned if our feistel permutation
// was unable to cycle-walk to a valid ticket number. We allow up to 128 attempts per input,
// which should make the chances of not finding an in-range input one in 3 × 10³⁸ which
// we might expect to be impossible within the lifetime of the universe.
// But we can handle it gracefully anyway:
address winner = address(0);
if (winners[i] == type(uint32).max) {
// We don't expect to arrive here, but a position in a permutation that will not resolve
// means there is no winner for this prize. We represent this with address(0). We need to
// include this in the winners array so that all prizes can be drawn and we can advance the
// raffle status.
emit NoWinner(raffleId_, start + i);
} else {
winner = _findTicketOwner(raffleId_, winners[i]);
}
_awardPrize(raffleId_, winners[i], winner);
}
// If this was the final batch, advance the raffle status
if (raffle.prizes.items.length == totalToGenerate) {
raffle.status = RaffleStatus.DrawComplete;
emit LogRaffleStatus(raffleId_, RaffleStatus.DrawComplete);
}
emit WinningTicketsDrawnBatch(raffleId_, start, thisCount, winners);
}
/**
* @dev _findTicketOwner Finds which user owns a specific ticket number. This finds the winning
* ticket in the raffle by searching the ticket ranges using a binary search. This is called as part
* of the draw process. Note that it depends entirely on a static user enumeration. We ensure this
* during the draw process by pausing deposits and withdrawals. Outside of the draw process this method
* is not guaranteed to return the correct owner for a historic ticket. For this reason it is kept internal.
* @param raffleId_ The raffle ID
* @param ticketNumber_ The ticket number to find
* @return ticketOwner_ The address of the user who owns this ticket
*/
function _findTicketOwner(
uint256 raffleId_,
uint256 ticketNumber_
) internal view returns (address) {
uint256 n = userBalances.length();
if (n == 0) return address(0);
uint256 left = 0;
uint256 right = n; // search in [left, right)
// lower_bound for first index with ranges[i] > ticketNumber_
while (left < right) {
uint256 mid = (left + right) >> 1;
if (raffles[raffleId_].userTickets.ranges[mid] <= ticketNumber_) {
left = mid + 1;
} else {
right = mid;
}
}
if (left == n) {
return address(0); // t >= total tickets
}
(address user, ) = userBalances.at(left);
return user;
}
/**
* @dev _awardPrize Awards a prize to a winner
* @param raffleId_ The raffle ID
* @param ticketNumber_ The ticket number of the prize winner
* @param winner_ The winner address
*/
function _awardPrize(
uint256 raffleId_,
uint32 ticketNumber_,
address winner_
) internal {
Raffle storage raffle = raffles[raffleId_];
// Add prize to the prizes array
uint256 prizeIndex = raffle.prizes.items.length;
raffle.prizes.items.push(
PrizeItem({
winner: winner_,
ticketNumber: ticketNumber_,
prizeIndex: uint32(prizeIndex),
prizeStatus: PrizeStatus.NotPaid
})
);
// Add raffle to user's winning raffles:
if (userPrizes[winner_][raffleId_].length == 0) {
winningRafflesForUser[winner_].push(raffleId_);
}
// Add prize index to user's prizes for this raffle:
userPrizes[winner_][raffleId_].push(prizeIndex);
emit PrizeAwarded(raffleId_, winner_, prizeIndex, ticketNumber_);
}
/**
* ------------------------------------------------
* (4) DRAW STEP FOUR - Batchable
* ------------------------------------------------
* @dev distributeAllPrizesForRaffleBatch Distributes NFTs for a raffle in batches.
* @param raffleId_ The raffle ID
* @param batchSize_ Number of prizes to process in this batch
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function distributeAllPrizesForRaffleBatch(
uint256 raffleId_,
uint256 batchSize_,
uint256 maxGasForNativeTokenTransfer_
) external onlyRole(BATCH_OPERATOR_ROLE) {
Raffle storage raffle = raffles[raffleId_];
uint256 startIndex = raffle.prizes.autoProcessedCount;
uint256 processedCount = _distributeAllPrizesForRaffleBatch(
raffle,
raffleId_,
batchSize_,
startIndex,
false,
maxGasForNativeTokenTransfer_
);
raffle.prizes.autoProcessedCount += processedCount;
if (raffle.prizes.autoProcessedCount == raffle.prizes.items.length) {
_unlock();
emit AutoDistributionComplete(raffleId_);
}
}
/**
* @dev distributeAllPrizesForRaffleWithStartIndexBatch Distributes NFTs for a raffle in batches.
* This method is public and can be called by any address once the draw is complete.
* Specify a start index and a batch size to distribute the prizes in batches. Prizes
* can only be transferred once, specifying a range where all items have already been
* transferred will revert.
* @param raffleId_ The raffle ID
* @param batchSize_ Number of prizes to process in this batch
* @param startIndex_ The index to start distributing prizes from
* @param revertWhenNothingToDistribute_ Whether to revert if no prizes are distributed. This
* protects against a call wasting gas when processing an index and count that will not
* produce any prizes, but allows auto processing to continue through batches in an
* orderly fashion, even if there are no prizes to distribute.
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function distributeAllPrizesForRaffleWithStartIndexBatch(
uint256 raffleId_,
uint256 batchSize_,
uint256 startIndex_,
bool revertWhenNothingToDistribute_,
uint256 maxGasForNativeTokenTransfer_
) external {
Raffle storage raffle = raffles[raffleId_];
_distributeAllPrizesForRaffleBatch(
raffle,
raffleId_,
batchSize_,
startIndex_,
revertWhenNothingToDistribute_,
maxGasForNativeTokenTransfer_
);
}
/**
* @dev _distributeAllPrizesForRaffleBatch Distributes NFTs for a raffle in batches.
* @param raffle_ The raffle
* @param raffleId_ The raffle ID
* @param batchSize_ Number of prizes to process in this batch
* @param startIndex_ The index to start distributing prizes from
* @param revertWhenNothingToDistribute_ Whether to revert if no prizes are distributed. This
* protects against a call wasting gas when processing an index and count that will not
* produce any prizes, but allows auto processing to continue through batches in an
* orderly fashion, even if there are no prizes to distribute.
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
* @return processedCount_ The number of prizes processed
*/
function _distributeAllPrizesForRaffleBatch(
Raffle storage raffle_,
uint256 raffleId_,
uint256 batchSize_,
uint256 startIndex_,
bool revertWhenNothingToDistribute_,
uint256 maxGasForNativeTokenTransfer_
)
internal
nonReentrant
onlyValidRaffleId(raffleId_)
returns (uint256 processedCount_)
{
uint256 indexLength = _validateAndCalculateIndexLength(
startIndex_,
batchSize_,
raffle_.prizes.items.length
);
uint256 distributionCount;
address nftContract = raffle_.config.nftAddress;
for (uint256 i = 0; i < indexLength; i++) {
if (
_distributePrize(
raffle_,
raffleId_,
nftContract,
startIndex_ + i,
maxGasForNativeTokenTransfer_
)
) {
distributionCount++;
}
processedCount_++;
}
if (revertWhenNothingToDistribute_ && distributionCount == 0) {
revert("No prizes to distribute in this batch");
}
raffle_.prizes.distributedCount += distributionCount;
emit PrizesDistributed(
raffleId_,
startIndex_,
indexLength,
distributionCount
);
return processedCount_;
}
/**
* @dev distributeSinglePrize Distributes a single prize for a raffle
* @param raffleId_ The raffle ID
* @param prizeIndex_ The index of the prize to distribute
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function distributeSinglePrize(
uint256 raffleId_,
uint256 prizeIndex_,
uint256 maxGasForNativeTokenTransfer_
) external nonReentrant onlyValidRaffleId(raffleId_) {
Raffle storage raffle = raffles[raffleId_];
if (prizeIndex_ >= raffle.prizes.items.length) {
revert("Invalid prize index");
}
if (
_distributePrize(
raffle,
raffleId_,
raffle.config.nftAddress,
prizeIndex_,
maxGasForNativeTokenTransfer_
)
) {
raffle.prizes.distributedCount++;
}
}
/**
* @dev _distributePrize Distributes a single prize for a raffle
* @param raffle_ The raffle to distribute the prize for
* @param raffleId_ The raffle ID
* @param nftContract_ The NFT contract address
* @param prizeIndex_ The index of the prize to distribute
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
* @return transferred_ Whether the prize was transferred or not
*/
function _distributePrize(
Raffle storage raffle_,
uint256 raffleId_,
address nftContract_,
uint256 prizeIndex_,
uint256 maxGasForNativeTokenTransfer_
) internal onlyBatchOperatorWhenLocked returns (bool transferred_) {
PrizeItem storage prize = raffle_.prizes.items[prizeIndex_];
// We can't run this before we are at status DrawComplete.
if (raffle_.status != RaffleStatus.DrawComplete) {
revert("Draw is not complete");
}
// If the prize has already been distributed we return false to indicate that the prize was
// not transferred as part of this processing. We can do this return early as a prize that is
// distributed must have already been paid for.
if (prize.prizeStatus == PrizeStatus.Distributed) {
return false;
}
address winner = prize.winner;
// We have an infinitesimally small chance of a non-winning ticket where the feistel
// permutation cannot walk to a suitable value. In this instance the winner is set as
// address(0) and we should not attempt to process either a payment or an NFT transfer.
// We return false to indicate that the prize was not transferred.
if (winner == address(0)) {
return false;
}
uint256 ticketNumber = prize.ticketNumber;
if (prize.prizeStatus == PrizeStatus.NotPaid) {
_processPayment(raffle_, winner, maxGasForNativeTokenTransfer_);
prize.prizeStatus = PrizeStatus.ReadyForDistribution;
emit PrizeStatusUpdated(
raffleId_,
prizeIndex_,
winner,
ticketNumber,
PrizeStatus.ReadyForDistribution
);
}
if (prize.prizeStatus == PrizeStatus.ReadyForDistribution) {
if (
_transferNft(nftContract_, winner, raffle_.config.tokenIds[prizeIndex_])
) {
prize.prizeStatus = PrizeStatus.Distributed;
emit PrizeStatusUpdated(
raffleId_,
prizeIndex_,
winner,
ticketNumber,
PrizeStatus.Distributed
);
// We have transferred the NFT to the winner so we return true.
return true;
}
}
// We have not transferred the NFT to the winner so we return false.
return false;
}
/**
* @dev _processPayment Process the payment associated with a prize
* @param raffle_ The raffle
* @param winner_ The winner address
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function _processPayment(
Raffle storage raffle_,
address winner_,
uint256 maxGasForNativeTokenTransfer_
) internal {
// Deduct ticket price from winner's balance
uint256 currentBalance = userBalances.get(winner_);
uint256 ticketPrice = raffle_.config.ticketPrice;
uint256 newBalance = currentBalance - ticketPrice;
if (newBalance == 0) {
userBalances.remove(winner_);
} else {
userBalances.set(winner_, newBalance);
}
// We have adjusted balances so we need to update the deposit balances last changed timestamp:
depositBalancesLastChangedTimestamp = block.timestamp;
// Calculate and distribute fees
uint256 raffleFee = (ticketPrice * raffle_.config.raffleFeePercentage) /
BASIS_POINTS_DENOMINATOR;
uint256 projectFee = ticketPrice - raffleFee;
// Add the fee amount to the accumulated platform fees:
if (raffleFee > 0) {
accumulatedPlatformFees += raffleFee;
}
// Transfer project fee to project treasury
if (projectFee > 0) {
_unstoppableTransfer(
raffle_.config.projectTreasury,
projectFee,
maxGasForNativeTokenTransfer_
);
}
}
/**
* @dev distributeUserPrizes Distributes the prizes for a user across all their winning raffles
* Be aware that if the user has won in a LOT of raffles this method may use a lot of gas and/or
* revert with out of gas. This shouldn't be an issue: the platform is designed to batch transfer
* NFTs as part of the draw process. Also in all cases a user can distribute prizes on a per raffle
* basis using distributeUserPrizesForRaffle.
* @param user_ The user to distribute prizes for
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function distributeUserPrizes(
address user_,
uint256 maxGasForNativeTokenTransfer_
) external nonReentrant {
uint256 distributedCount = 0;
uint256 count = winningRafflesForUser[user_].length;
for (uint256 i = 0; i < count; i++) {
uint256 raffleId = winningRafflesForUser[user_][i];
distributedCount += _distributeUserPrizesForRaffle(
user_,
raffleId,
maxGasForNativeTokenTransfer_
);
}
if (distributedCount == 0) {
revert("No prizes for user to distribute");
}
}
/**
* @dev distributeUserPrizesForRaffle Distributes the prizes for a user for a specific raffle
* This method is public and can be called by any address.
* @param user_ The user to distribute prizes for
* @param raffleId_ The raffle to distribute prizes for
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
*/
function distributeUserPrizesForRaffle(
address user_,
uint256 raffleId_,
uint256 maxGasForNativeTokenTransfer_
) external nonReentrant onlyValidRaffleId(raffleId_) {
uint256 distributedCount = _distributeUserPrizesForRaffle(
user_,
raffleId_,
maxGasForNativeTokenTransfer_
);
if (distributedCount == 0) {
revert("No prizes for user in raffle to distribute");
}
}
/**
* @dev distributeUserPrizesForRaffle Distributes the prizes for a user for a specific raffle.
* @param user_ The user to distribute prizes for
* @param raffleId_ The raffle to distribute prizes for
* @param maxGasForNativeTokenTransfer_ The maximum gas to use for the transfer.
* @return distributedCount_ The number of prizes distributed
*/
function _distributeUserPrizesForRaffle(
address user_,
uint256 raffleId_,
uint256 maxGasForNativeTokenTransfer_
) internal returns (uint256 distributedCount_) {
Raffle storage raffle = raffles[raffleId_];
// Get the user's prize indexes for this raffle:
uint256[] storage userPrizeIndexes = userPrizes[user_][raffleId_];
address nftContract = raffle.config.nftAddress;
uint256 count = userPrizeIndexes.length;
for (uint256 i = 0; i < count; i++) {
uint256 prizeIndex = userPrizeIndexes[i];
if (
_distributePrize(
raffle,
raffleId_,
nftContract,
prizeIndex,
maxGasForNativeTokenTransfer_
)
) {
distributedCount_++;
}
}
raffle.prizes.distributedCount += distributedCount_;
emit UserPrizesDistributedForRaffle(raffleId_, user_, distributedCount_);
return (distributedCount_);
}
/**
* =============================
* === Helper/Misc Functions ===
* =============================
*/
/**
* @dev _transferNft Transfers a single NFT from treasury to user
* @param nftAddress_ The NFT contract address
* @param to_ The recipient address
* @param tokenId_ The token ID to transfer
*/
function _transferNft(
address nftAddress_,
address to_,
uint256 tokenId_
) internal returns (bool success_) {
// We call this with try as we don't revert the entire batch on a single failure,
// rather we leave the NFT marked as unclaimed and will move on to the next prize.
try IERC721(nftAddress_).transferFrom(nftTreasury, to_, tokenId_) {
return true;
} catch {
return false;
}
}
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external pure returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
receive() external payable {
revert("Cannot receive ETH directly, use payable methods");
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.7.6;
library ExcessivelySafeCall {
uint256 constant LOW_28_MASK =
0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _value The value in wei to send to the remote contract
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeCall(
address _target,
uint256 _gas,
uint256 _value,
uint16 _maxCopy,
bytes memory _calldata
) internal returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid memcopying a very large returndata
// returned by a malicious contract
assembly {
_success := call(
_gas, // gas
_target, // recipient
_value, // ether value
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returnData, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
/// @notice Use when you _really_ really _really_ don't trust the called
/// contract. This prevents the called contract from causing reversion of
/// the caller in as many ways as we can.
/// @dev The main difference between this and a solidity low-level call is
/// that we limit the number of bytes that the callee can cause to be
/// copied to caller memory. This prevents stupid things like malicious
/// contracts returning 10,000,000 bytes causing a local OOG when copying
/// to memory.
/// @param _target The address to call
/// @param _gas The amount of gas to forward to the remote contract
/// @param _maxCopy The maximum number of bytes of returndata to copy
/// to memory.
/// @param _calldata The data to send to the remote contract
/// @return success and returndata, as `.call()`. Returndata is capped to
/// `_maxCopy` bytes.
function excessivelySafeStaticCall(
address _target,
uint256 _gas,
uint16 _maxCopy,
bytes memory _calldata
) internal view returns (bool, bytes memory) {
// set up for assembly call
uint256 _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
// dispatch message to recipient
// by assembly calling "handle" function
// we call via assembly to avoid memcopying a very large returndata
// returned by a malicious contract
assembly {
_success := staticcall(
_gas, // gas
_target, // recipient
add(_calldata, 0x20), // inloc
mload(_calldata), // inlen
0, // outloc
0 // outlen
)
// limit our copy to 256 bytes
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
// Store the length of the copied bytes
mstore(_returnData, _toCopy)
// copy the bytes from returndata[0:_toCopy]
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
/**
* @notice Swaps function selectors in encoded contract calls
* @dev Allows reuse of encoded calldata for functions with identical
* argument types but different names. It simply swaps out the first 4 bytes
* for the new selector. This function modifies memory in place, and should
* only be used with caution.
* @param _newSelector The new 4-byte selector
* @param _buf The encoded contract args
*/
function swapSelector(bytes4 _newSelector, bytes memory _buf)
internal
pure
{
require(_buf.length >= 4);
uint256 _mask = LOW_28_MASK;
assembly {
// load the first word of
let _word := mload(add(_buf, 0x20))
// mask out the top 4 bytes
// /x
_word := and(_word, _mask)
_word := or(_newSelector, _word)
mstore(add(_buf, 0x20), _word)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {IERC165, 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);
_;
}
/// @inheritdoc IERC165
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` from `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.4.0) (access/extensions/AccessControlDefaultAdminRules.sol)
pragma solidity ^0.8.20;
import {IAccessControlDefaultAdminRules} from "./IAccessControlDefaultAdminRules.sol";
import {AccessControl, IAccessControl} from "../AccessControl.sol";
import {SafeCast} from "../../utils/math/SafeCast.sol";
import {Math} from "../../utils/math/Math.sol";
import {IERC5313} from "../../interfaces/IERC5313.sol";
import {IERC165} from "../../utils/introspection/ERC165.sol";
/**
* @dev Extension of {AccessControl} that allows specifying special rules to manage
* the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions
* over other roles that may potentially have privileged rights in the system.
*
* If a specific role doesn't have an admin role assigned, the holder of the
* `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it.
*
* This contract implements the following risk mitigations on top of {AccessControl}:
*
* * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced.
* * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account.
* * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted.
* * The delay can be changed by scheduling, see {changeDefaultAdminDelay}.
* * Role transfers must wait at least one block after scheduling before it can be accepted.
* * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`.
*
* Example usage:
*
* ```solidity
* contract MyToken is AccessControlDefaultAdminRules {
* constructor() AccessControlDefaultAdminRules(
* 3 days,
* msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder
* ) {}
* }
* ```
*/
abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl {
// pending admin pair read/written together frequently
address private _pendingDefaultAdmin;
uint48 private _pendingDefaultAdminSchedule; // 0 == unset
uint48 private _currentDelay;
address private _currentDefaultAdmin;
// pending delay pair read/written together frequently
uint48 private _pendingDelay;
uint48 private _pendingDelaySchedule; // 0 == unset
/**
* @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
*/
constructor(uint48 initialDelay, address initialDefaultAdmin) {
if (initialDefaultAdmin == address(0)) {
revert AccessControlInvalidDefaultAdmin(address(0));
}
_currentDelay = initialDelay;
_grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId);
}
/// @inheritdoc IERC5313
function owner() public view virtual returns (address) {
return defaultAdmin();
}
///
/// Override AccessControl role management
///
/**
* @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
*/
function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlEnforcedDefaultAdminRules();
}
super.grantRole(role, account);
}
/**
* @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
*/
function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlEnforcedDefaultAdminRules();
}
super.revokeRole(role, account);
}
/**
* @dev See {AccessControl-renounceRole}.
*
* For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling
* {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule
* has also passed when calling this function.
*
* After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions.
*
* NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin},
* thereby disabling any functionality that is only available for it, and the possibility of reassigning a
* non-administrated role.
*/
function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
(address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
revert AccessControlEnforcedDefaultAdminDelay(schedule);
}
delete _pendingDefaultAdminSchedule;
}
super.renounceRole(role, account);
}
/**
* @dev See {AccessControl-_grantRole}.
*
* For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the
* role has been previously renounced.
*
* NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`
* assignable again. Make sure to guarantee this is the expected behavior in your implementation.
*/
function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
if (role == DEFAULT_ADMIN_ROLE) {
if (defaultAdmin() != address(0)) {
revert AccessControlEnforcedDefaultAdminRules();
}
_currentDefaultAdmin = account;
}
return super._grantRole(role, account);
}
/// @inheritdoc AccessControl
function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
delete _currentDefaultAdmin;
}
return super._revokeRole(role, account);
}
/**
* @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlEnforcedDefaultAdminRules();
}
super._setRoleAdmin(role, adminRole);
}
///
/// AccessControlDefaultAdminRules accessors
///
/// @inheritdoc IAccessControlDefaultAdminRules
function defaultAdmin() public view virtual returns (address) {
return _currentDefaultAdmin;
}
/// @inheritdoc IAccessControlDefaultAdminRules
function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {
return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule);
}
/// @inheritdoc IAccessControlDefaultAdminRules
function defaultAdminDelay() public view virtual returns (uint48) {
uint48 schedule = _pendingDelaySchedule;
return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;
}
/// @inheritdoc IAccessControlDefaultAdminRules
function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {
schedule = _pendingDelaySchedule;
return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0);
}
/// @inheritdoc IAccessControlDefaultAdminRules
function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {
return 5 days;
}
///
/// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin
///
/// @inheritdoc IAccessControlDefaultAdminRules
function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_beginDefaultAdminTransfer(newAdmin);
}
/**
* @dev See {beginDefaultAdminTransfer}.
*
* Internal function without access restriction.
*/
function _beginDefaultAdminTransfer(address newAdmin) internal virtual {
uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay();
_setPendingDefaultAdmin(newAdmin, newSchedule);
emit DefaultAdminTransferScheduled(newAdmin, newSchedule);
}
/// @inheritdoc IAccessControlDefaultAdminRules
function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_cancelDefaultAdminTransfer();
}
/**
* @dev See {cancelDefaultAdminTransfer}.
*
* Internal function without access restriction.
*/
function _cancelDefaultAdminTransfer() internal virtual {
_setPendingDefaultAdmin(address(0), 0);
}
/// @inheritdoc IAccessControlDefaultAdminRules
function acceptDefaultAdminTransfer() public virtual {
(address newDefaultAdmin, ) = pendingDefaultAdmin();
if (_msgSender() != newDefaultAdmin) {
// Enforce newDefaultAdmin explicit acceptance.
revert AccessControlInvalidDefaultAdmin(_msgSender());
}
_acceptDefaultAdminTransfer();
}
/**
* @dev See {acceptDefaultAdminTransfer}.
*
* Internal function without access restriction.
*/
function _acceptDefaultAdminTransfer() internal virtual {
(address newAdmin, uint48 schedule) = pendingDefaultAdmin();
if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
revert AccessControlEnforcedDefaultAdminDelay(schedule);
}
_revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());
_grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
delete _pendingDefaultAdmin;
delete _pendingDefaultAdminSchedule;
}
///
/// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay
///
/// @inheritdoc IAccessControlDefaultAdminRules
function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_changeDefaultAdminDelay(newDelay);
}
/**
* @dev See {changeDefaultAdminDelay}.
*
* Internal function without access restriction.
*/
function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {
uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay);
_setPendingDelay(newDelay, newSchedule);
emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);
}
/// @inheritdoc IAccessControlDefaultAdminRules
function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
_rollbackDefaultAdminDelay();
}
/**
* @dev See {rollbackDefaultAdminDelay}.
*
* Internal function without access restriction.
*/
function _rollbackDefaultAdminDelay() internal virtual {
_setPendingDelay(0, 0);
}
/**
* @dev Returns the amount of seconds to wait after the `newDelay` will
* become the new {defaultAdminDelay}.
*
* The value returned guarantees that if the delay is reduced, it will go into effect
* after a wait that honors the previously set delay.
*
* See {defaultAdminDelayIncreaseWait}.
*/
function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {
uint48 currentDelay = defaultAdminDelay();
// When increasing the delay, we schedule the delay change to occur after a period of "new delay" has passed, up
// to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day
// to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new
// delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like
// using milliseconds instead of seconds.
//
// When decreasing the delay, we wait the difference between "current delay" and "new delay". This guarantees
// that an admin transfer cannot be made faster than "current delay" at the time the delay change is scheduled.
// For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.
return
newDelay > currentDelay
? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48
: currentDelay - newDelay;
}
///
/// Private setters
///
/**
* @dev Setter of the tuple for pending admin and its schedule.
*
* May emit a DefaultAdminTransferCanceled event.
*/
function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {
(, uint48 oldSchedule) = pendingDefaultAdmin();
_pendingDefaultAdmin = newAdmin;
_pendingDefaultAdminSchedule = newSchedule;
// An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.
if (_isScheduleSet(oldSchedule)) {
// Emit for implicit cancellations when another default admin was scheduled.
emit DefaultAdminTransferCanceled();
}
}
/**
* @dev Setter of the tuple for pending delay and its schedule.
*
* May emit a DefaultAdminDelayChangeCanceled event.
*/
function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {
uint48 oldSchedule = _pendingDelaySchedule;
if (_isScheduleSet(oldSchedule)) {
if (_hasSchedulePassed(oldSchedule)) {
// Materialize a virtual delay
_currentDelay = _pendingDelay;
} else {
// Emit for implicit cancellations when another delay was scheduled.
emit DefaultAdminDelayChangeCanceled();
}
}
_pendingDelay = newDelay;
_pendingDelaySchedule = newSchedule;
}
///
/// Private helpers
///
/**
* @dev Defines if an `schedule` is considered set. For consistency purposes.
*/
function _isScheduleSet(uint48 schedule) private pure returns (bool) {
return schedule != 0;
}
/**
* @dev Defines if an `schedule` is considered passed. For consistency purposes.
*/
function _hasSchedulePassed(uint48 schedule) private view returns (bool) {
return schedule < block.timestamp;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/extensions/IAccessControlDefaultAdminRules.sol)
pragma solidity >=0.8.4;
import {IAccessControl} from "../IAccessControl.sol";
/**
* @dev External interface of AccessControlDefaultAdminRules declared to support ERC-165 detection.
*/
interface IAccessControlDefaultAdminRules is IAccessControl {
/**
* @dev The new default admin is not a valid default admin.
*/
error AccessControlInvalidDefaultAdmin(address defaultAdmin);
/**
* @dev At least one of the following rules was violated:
*
* - The `DEFAULT_ADMIN_ROLE` must only be managed by itself.
* - The `DEFAULT_ADMIN_ROLE` must only be held by one account at the time.
* - Any `DEFAULT_ADMIN_ROLE` transfer must be in two delayed steps.
*/
error AccessControlEnforcedDefaultAdminRules();
/**
* @dev The delay for transferring the default admin delay is enforced and
* the operation must wait until `schedule`.
*
* NOTE: `schedule` can be 0 indicating there's no transfer scheduled.
*/
error AccessControlEnforcedDefaultAdminDelay(uint48 schedule);
/**
* @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next
* address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule`
* passes.
*/
event DefaultAdminTransferScheduled(address indexed newAdmin, uint48 acceptSchedule);
/**
* @dev Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule.
*/
event DefaultAdminTransferCanceled();
/**
* @dev Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next
* delay to be applied between default admin transfer after `effectSchedule` has passed.
*/
event DefaultAdminDelayChangeScheduled(uint48 newDelay, uint48 effectSchedule);
/**
* @dev Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass.
*/
event DefaultAdminDelayChangeCanceled();
/**
* @dev Returns the address of the current `DEFAULT_ADMIN_ROLE` holder.
*/
function defaultAdmin() external view returns (address);
/**
* @dev Returns a tuple of a `newAdmin` and an accept schedule.
*
* After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role
* by calling {acceptDefaultAdminTransfer}, completing the role transfer.
*
* A zero value only in `acceptSchedule` indicates no pending admin transfer.
*
* NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced.
*/
function pendingDefaultAdmin() external view returns (address newAdmin, uint48 acceptSchedule);
/**
* @dev Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started.
*
* This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set
* the acceptance schedule.
*
* NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this
* function returns the new delay. See {changeDefaultAdminDelay}.
*/
function defaultAdminDelay() external view returns (uint48);
/**
* @dev Returns a tuple of `newDelay` and an effect schedule.
*
* After the `schedule` passes, the `newDelay` will get into effect immediately for every
* new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}.
*
* A zero value only in `effectSchedule` indicates no pending delay change.
*
* NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay}
* will be zero after the effect schedule.
*/
function pendingDefaultAdminDelay() external view returns (uint48 newDelay, uint48 effectSchedule);
/**
* @dev Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance
* after the current timestamp plus a {defaultAdminDelay}.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* Emits a DefaultAdminRoleChangeStarted event.
*/
function beginDefaultAdminTransfer(address newAdmin) external;
/**
* @dev Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
*
* A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* May emit a DefaultAdminTransferCanceled event.
*/
function cancelDefaultAdminTransfer() external;
/**
* @dev Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
*
* After calling the function:
*
* - `DEFAULT_ADMIN_ROLE` should be granted to the caller.
* - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder.
* - {pendingDefaultAdmin} should be reset to zero values.
*
* Requirements:
*
* - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`.
* - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed.
*/
function acceptDefaultAdminTransfer() external;
/**
* @dev Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting
* into effect after the current timestamp plus a {defaultAdminDelay}.
*
* This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this
* method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay}
* set before calling.
*
* The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then
* calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin}
* complete transfer (including acceptance).
*
* The schedule is designed for two scenarios:
*
* - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by
* {defaultAdminDelayIncreaseWait}.
* - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`.
*
* A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event.
*/
function changeDefaultAdminDelay(uint48 newDelay) external;
/**
* @dev Cancels a scheduled {defaultAdminDelay} change.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* May emit a DefaultAdminDelayChangeCanceled event.
*/
function rollbackDefaultAdminDelay() external;
/**
* @dev Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay})
* to take effect. Default to 5 days.
*
* When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with
* the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds)
* that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can
* be overrode for a custom {defaultAdminDelay} increase scheduling.
*
* IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise,
* there's a risk of setting a high new delay that goes into effect almost immediately without the
* possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds).
*/
function defaultAdminDelayIncreaseWait() external view returns (uint48);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/IAccessControl.sol)
pragma solidity >=0.8.4;
/**
* @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 to signal 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.4.0) (interfaces/IERC5313.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface for the Light Contract Ownership Standard.
*
* A standardized minimal interface required to identify an account that controls a contract
*/
interface IERC5313 {
/**
* @dev Gets the address of the owner.
*/
function owner() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @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.4.0) (token/ERC721/IERC721.sol)
pragma solidity >=0.6.2;
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.4.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity >=0.5.0;
/**
* @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.4.0) (utils/Arrays.sol)
// This file was procedurally generated from scripts/generate/templates/Arrays.js.
pragma solidity ^0.8.20;
import {Comparators} from "./Comparators.sol";
import {SlotDerivation} from "./SlotDerivation.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";
/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using SlotDerivation for bytes32;
using StorageSlot for bytes32;
/**
* @dev Sort an array of uint256 (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
uint256[] memory array,
function(uint256, uint256) pure returns (bool) comp
) internal pure returns (uint256[] memory) {
_quickSort(_begin(array), _end(array), comp);
return array;
}
/**
* @dev Variant of {sort} that sorts an array of uint256 in increasing order.
*/
function sort(uint256[] memory array) internal pure returns (uint256[] memory) {
sort(array, Comparators.lt);
return array;
}
/**
* @dev Sort an array of address (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
address[] memory array,
function(address, address) pure returns (bool) comp
) internal pure returns (address[] memory) {
sort(_castToUint256Array(array), _castToUint256Comp(comp));
return array;
}
/**
* @dev Variant of {sort} that sorts an array of address in increasing order.
*/
function sort(address[] memory array) internal pure returns (address[] memory) {
sort(_castToUint256Array(array), Comparators.lt);
return array;
}
/**
* @dev Sort an array of bytes32 (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
bytes32[] memory array,
function(bytes32, bytes32) pure returns (bool) comp
) internal pure returns (bytes32[] memory) {
sort(_castToUint256Array(array), _castToUint256Comp(comp));
return array;
}
/**
* @dev Variant of {sort} that sorts an array of bytes32 in increasing order.
*/
function sort(bytes32[] memory array) internal pure returns (bytes32[] memory) {
sort(_castToUint256Array(array), Comparators.lt);
return array;
}
/**
* @dev Performs a quick sort of a segment of memory. The segment sorted starts at `begin` (inclusive), and stops
* at end (exclusive). Sorting follows the `comp` comparator.
*
* Invariant: `begin <= end`. This is the case when initially called by {sort} and is preserved in subcalls.
*
* IMPORTANT: Memory locations between `begin` and `end` are not validated/zeroed. This function should
* be used only if the limits are within a memory array.
*/
function _quickSort(uint256 begin, uint256 end, function(uint256, uint256) pure returns (bool) comp) private pure {
unchecked {
if (end - begin < 0x40) return;
// Use first element as pivot
uint256 pivot = _mload(begin);
// Position where the pivot should be at the end of the loop
uint256 pos = begin;
for (uint256 it = begin + 0x20; it < end; it += 0x20) {
if (comp(_mload(it), pivot)) {
// If the value stored at the iterator's position comes before the pivot, we increment the
// position of the pivot and move the value there.
pos += 0x20;
_swap(pos, it);
}
}
_swap(begin, pos); // Swap pivot into place
_quickSort(begin, pos, comp); // Sort the left side of the pivot
_quickSort(pos + 0x20, end, comp); // Sort the right side of the pivot
}
}
/**
* @dev Pointer to the memory location of the first element of `array`.
*/
function _begin(uint256[] memory array) private pure returns (uint256 ptr) {
assembly ("memory-safe") {
ptr := add(array, 0x20)
}
}
/**
* @dev Pointer to the memory location of the first memory word (32bytes) after `array`. This is the memory word
* that comes just after the last element of the array.
*/
function _end(uint256[] memory array) private pure returns (uint256 ptr) {
unchecked {
return _begin(array) + array.length * 0x20;
}
}
/**
* @dev Load memory word (as a uint256) at location `ptr`.
*/
function _mload(uint256 ptr) private pure returns (uint256 value) {
assembly {
value := mload(ptr)
}
}
/**
* @dev Swaps the elements memory location `ptr1` and `ptr2`.
*/
function _swap(uint256 ptr1, uint256 ptr2) private pure {
assembly {
let value1 := mload(ptr1)
let value2 := mload(ptr2)
mstore(ptr1, value2)
mstore(ptr2, value1)
}
}
/// @dev Helper: low level cast address memory array to uint256 memory array
function _castToUint256Array(address[] memory input) private pure returns (uint256[] memory output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast bytes32 memory array to uint256 memory array
function _castToUint256Array(bytes32[] memory input) private pure returns (uint256[] memory output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast address comp function to uint256 comp function
function _castToUint256Comp(
function(address, address) pure returns (bool) input
) private pure returns (function(uint256, uint256) pure returns (bool) output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast bytes32 comp function to uint256 comp function
function _castToUint256Comp(
function(bytes32, bytes32) pure returns (bool) input
) private pure returns (function(uint256, uint256) pure returns (bool) output) {
assembly {
output := input
}
}
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. 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).
*
* NOTE: The `array` is expected to be sorted in ascending order, and to
* contain no repeated elements.
*
* IMPORTANT: Deprecated. This implementation behaves as {lowerBound} but lacks
* support for repeated elements in the array. The {lowerBound} function should
* be used instead.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
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 towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
/**
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value greater or equal than `element`. 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).
*
* See C++'s https://en.cppreference.com/w/cpp/algorithm/lower_bound[lower_bound].
*/
function lowerBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
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 towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value < element) {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
} else {
high = mid;
}
}
return low;
}
/**
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value strictly greater than `element`. 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).
*
* See C++'s https://en.cppreference.com/w/cpp/algorithm/upper_bound[upper_bound].
*/
function upperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
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 towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
}
}
return low;
}
/**
* @dev Same as {lowerBound}, but with an array in memory.
*/
function lowerBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
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 towards zero (it does integer division with truncation).
if (unsafeMemoryAccess(array, mid) < element) {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
} else {
high = mid;
}
}
return low;
}
/**
* @dev Same as {upperBound}, but with an array in memory.
*/
function upperBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
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 towards zero (it does integer division with truncation).
if (unsafeMemoryAccess(array, mid) > element) {
high = mid;
} else {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
}
}
return low;
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getAddressSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getBytes32Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getUint256Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes[] storage arr, uint256 pos) internal pure returns (StorageSlot.BytesSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getBytesSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(string[] storage arr, uint256 pos) internal pure returns (StorageSlot.StringSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getStringSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(bytes32[] memory arr, uint256 pos) internal pure returns (bytes32 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(bytes[] memory arr, uint256 pos) internal pure returns (bytes memory res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(string[] memory arr, uint256 pos) internal pure returns (string memory res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(address[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(bytes32[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(uint256[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(bytes[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(string[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Comparators.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides a set of functions to compare values.
*
* _Available since v5.1._
*/
library Comparators {
function lt(uint256 a, uint256 b) internal pure returns (bool) {
return a < b;
}
function gt(uint256 a, uint256 b) internal pure returns (bool) {
return a > b;
}
}// 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.4.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 {
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @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.3.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 Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = 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 = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @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 {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(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 {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @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 {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 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 low / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, 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 ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, 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 high into low.
low |= high * 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 high
// is no longer required.
result = low * 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 Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;
// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
}
/**
* @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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
}
/**
* @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.1.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
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
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.1.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
// OpenZeppelin Contracts (last updated v5.3.0) (utils/SlotDerivation.sol)
// This file was procedurally generated from scripts/generate/templates/SlotDerivation.js.
pragma solidity ^0.8.20;
/**
* @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots
* corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by
* the solidity language / compiler.
*
* See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.].
*
* Example usage:
* ```solidity
* contract Example {
* // Add the library methods
* using StorageSlot for bytes32;
* using SlotDerivation for bytes32;
*
* // Declare a namespace
* string private constant _NAMESPACE = "<namespace>"; // eg. OpenZeppelin.Slot
*
* function setValueInNamespace(uint256 key, address newValue) internal {
* _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value = newValue;
* }
*
* function getValueInNamespace(uint256 key) internal view returns (address) {
* return _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value;
* }
* }
* ```
*
* TIP: Consider using this library along with {StorageSlot}.
*
* NOTE: This library provides a way to manipulate storage locations in a non-standard way. Tooling for checking
* upgrade safety will ignore the slots accessed through this library.
*
* _Available since v5.1._
*/
library SlotDerivation {
/**
* @dev Derive an ERC-7201 slot from a string (namespace).
*/
function erc7201Slot(string memory namespace) internal pure returns (bytes32 slot) {
assembly ("memory-safe") {
mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
slot := and(keccak256(0x00, 0x20), not(0xff))
}
}
/**
* @dev Add an offset to a slot to get the n-th element of a structure or an array.
*/
function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) {
unchecked {
return bytes32(uint256(slot) + pos);
}
}
/**
* @dev Derive the location of the first element in an array from the slot where the length is stored.
*/
function deriveArray(bytes32 slot) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, slot)
result := keccak256(0x00, 0x20)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, and(key, shr(96, not(0))))
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, iszero(iszero(key)))
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
let length := mload(key)
let begin := add(key, 0x20)
let end := add(begin, length)
let cache := mload(end)
mstore(end, slot)
result := keccak256(begin, add(length, 0x20))
mstore(end, cache)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
let length := mload(key)
let begin := add(key, 0x20)
let end := add(begin, length)
let cache := mload(end)
mstore(end, slot)
result := keccak256(begin, add(length, 0x20))
mstore(end, cache)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.
pragma solidity ^0.8.20;
import {EnumerableSet} from "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
* - Map can be cleared (all entries removed) in O(n).
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
* - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
* - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
* - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
* - `uint256 -> bytes32` (`UintToBytes32Map`) since v5.1.0
* - `address -> address` (`AddressToAddressMap`) since v5.1.0
* - `address -> bytes32` (`AddressToBytes32Map`) since v5.1.0
* - `bytes32 -> address` (`Bytes32ToAddressMap`) since v5.1.0
* - `bytes -> bytes` (`BytesToBytesMap`) since v5.4.0
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
* ====
*/
library EnumerableMap {
using EnumerableSet for *;
// To implement this library for multiple types with as little code repetition as possible, we write it in
// terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
// and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit in bytes32.
/**
* @dev Query for a nonexistent map key.
*/
error EnumerableMapNonexistentKey(bytes32 key);
struct Bytes32ToBytes32Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 key => bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the map grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(Bytes32ToBytes32Map storage map) internal {
uint256 len = length(map);
for (uint256 i = 0; i < len; ++i) {
delete map._values[map._keys.at(i)];
}
map._keys.clear();
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32 key, bytes32 value) {
bytes32 atKey = map._keys.at(index);
return (atKey, map._values[atKey]);
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool exists, bytes32 value) {
bytes32 val = map._values[key];
if (val == bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, val);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
bytes32 value = map._values[key];
if (value == 0 && !contains(map, key)) {
revert EnumerableMapNonexistentKey(key);
}
return value;
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
return map._keys.values();
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(
Bytes32ToBytes32Map storage map,
uint256 start,
uint256 end
) internal view returns (bytes32[] memory) {
return map._keys.values(start, end);
}
// UintToUintMap
struct UintToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: This function has an unbounded cost that scales with map size. Developers should keep in mind that
* using it may render the function uncallable if the map grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function clear(UintToUintMap storage map) internal {
clear(map._inner);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToUintMap storage map, uint256 index) internal view returns (uint256 key, uint256 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (uint256(atKey), uint256(val));
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool exists, uint256 value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
return (success, uint256(val));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key)));
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToUintMap storage map, uint256 start, uint256 end) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner, start, end);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintToAddressMap
struct UintToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: This function has an unbounded cost that scales with map size. Developers should keep in mind that
* using it may render the function uncallable if the map grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function clear(UintToAddressMap storage map) internal {
clear(map._inner);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256 key, address value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (uint256(atKey), address(uint160(uint256(val))));
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool exists, address value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(val))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key)))));
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToAddressMap storage map, uint256 start, uint256 end) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner, start, end);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintToBytes32Map
struct UintToBytes32Map {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToBytes32Map storage map, uint256 key, bytes32 value) internal returns (bool) {
return set(map._inner, bytes32(key), value);
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToBytes32Map storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: This function has an unbounded cost that scales with map size. Developers should keep in mind that
* using it may render the function uncallable if the map grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function clear(UintToBytes32Map storage map) internal {
clear(map._inner);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToBytes32Map storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToBytes32Map storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToBytes32Map storage map, uint256 index) internal view returns (uint256 key, bytes32 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (uint256(atKey), val);
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToBytes32Map storage map, uint256 key) internal view returns (bool exists, bytes32 value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
return (success, val);
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToBytes32Map storage map, uint256 key) internal view returns (bytes32) {
return get(map._inner, bytes32(key));
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToBytes32Map storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToBytes32Map storage map, uint256 start, uint256 end) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner, start, end);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressToUintMap
struct AddressToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: This function has an unbounded cost that scales with map size. Developers should keep in mind that
* using it may render the function uncallable if the map grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function clear(AddressToUintMap storage map) internal {
clear(map._inner);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToUintMap storage map, uint256 index) internal view returns (address key, uint256 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (address(uint160(uint256(atKey))), uint256(val));
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool exists, uint256 value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(val));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToUintMap storage map, uint256 start, uint256 end) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner, start, end);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressToAddressMap
struct AddressToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToAddressMap storage map, address key, address value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToAddressMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: This function has an unbounded cost that scales with map size. Developers should keep in mind that
* using it may render the function uncallable if the map grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function clear(AddressToAddressMap storage map) internal {
clear(map._inner);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToAddressMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToAddressMap storage map, uint256 index) internal view returns (address key, address value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (address(uint160(uint256(atKey))), address(uint160(uint256(val))));
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToAddressMap storage map, address key) internal view returns (bool exists, address value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, address(uint160(uint256(val))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToAddressMap storage map, address key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(uint256(uint160(key)))))));
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToAddressMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(
AddressToAddressMap storage map,
uint256 start,
uint256 end
) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner, start, end);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressToBytes32Map
struct AddressToBytes32Map {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToBytes32Map storage map, address key, bytes32 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), value);
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToBytes32Map storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: This function has an unbounded cost that scales with map size. Developers should keep in mind that
* using it may render the function uncallable if the map grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function clear(AddressToBytes32Map storage map) internal {
clear(map._inner);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToBytes32Map storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToBytes32Map storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToBytes32Map storage map, uint256 index) internal view returns (address key, bytes32 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (address(uint160(uint256(atKey))), val);
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToBytes32Map storage map, address key) internal view returns (bool exists, bytes32 value) {
(bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, val);
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToBytes32Map storage map, address key) internal view returns (bytes32) {
return get(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToBytes32Map storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(
AddressToBytes32Map storage map,
uint256 start,
uint256 end
) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner, start, end);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// Bytes32ToUintMap
struct Bytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
return set(map._inner, key, bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: This function has an unbounded cost that scales with map size. Developers should keep in mind that
* using it may render the function uncallable if the map grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function clear(Bytes32ToUintMap storage map) internal {
clear(map._inner);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32 key, uint256 value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (atKey, uint256(val));
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool exists, uint256 value) {
(bool success, bytes32 val) = tryGet(map._inner, key);
return (success, uint256(val));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
return uint256(get(map._inner, key));
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToUintMap storage map, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner, start, end);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// Bytes32ToAddressMap
struct Bytes32ToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToAddressMap storage map, bytes32 key, address value) internal returns (bool) {
return set(map._inner, key, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToAddressMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: This function has an unbounded cost that scales with map size. Developers should keep in mind that
* using it may render the function uncallable if the map grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function clear(Bytes32ToAddressMap storage map) internal {
clear(map._inner);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToAddressMap storage map, uint256 index) internal view returns (bytes32 key, address value) {
(bytes32 atKey, bytes32 val) = at(map._inner, index);
return (atKey, address(uint160(uint256(val))));
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool exists, address value) {
(bool success, bytes32 val) = tryGet(map._inner, key);
return (success, address(uint160(uint256(val))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, key))));
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToAddressMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(
Bytes32ToAddressMap storage map,
uint256 start,
uint256 end
) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner, start, end);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Query for a nonexistent map key.
*/
error EnumerableMapNonexistentBytesKey(bytes key);
struct BytesToBytesMap {
// Storage of keys
EnumerableSet.BytesSet _keys;
mapping(bytes key => bytes) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(BytesToBytesMap storage map, bytes memory key, bytes memory value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(BytesToBytesMap storage map, bytes memory key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the map grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(BytesToBytesMap storage map) internal {
uint256 len = length(map);
for (uint256 i = 0; i < len; ++i) {
delete map._values[map._keys.at(i)];
}
map._keys.clear();
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(BytesToBytesMap storage map, bytes memory key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(BytesToBytesMap storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(
BytesToBytesMap storage map,
uint256 index
) internal view returns (bytes memory key, bytes memory value) {
key = map._keys.at(index);
value = map._values[key];
}
/**
* @dev Tries to return the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(
BytesToBytesMap storage map,
bytes memory key
) internal view returns (bool exists, bytes memory value) {
value = map._values[key];
exists = bytes(value).length != 0 || contains(map, key);
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(BytesToBytesMap storage map, bytes memory key) internal view returns (bytes memory value) {
bool exists;
(exists, value) = tryGet(map, key);
if (!exists) {
revert EnumerableMapNonexistentBytesKey(key);
}
}
/**
* @dev Returns an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(BytesToBytesMap storage map) internal view returns (bytes[] memory) {
return map._keys.values();
}
/**
* @dev Returns an array containing a slice of the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(BytesToBytesMap storage map, uint256 start, uint256 end) internal view returns (bytes[] memory) {
return map._keys.values(start, end);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
import {Arrays} from "../Arrays.sol";
import {Math} from "../math/Math.sol";
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
* - Set can be cleared (all elements removed) in O(n).
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* The following types are supported:
*
* - `bytes32` (`Bytes32Set`) since v3.3.0
* - `address` (`AddressSet`) since v3.3.0
* - `uint256` (`UintSet`) since v3.3.0
* - `string` (`StringSet`) since v5.4.0
* - `bytes` (`BytesSet`) since v5.4.0
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: This function has an unbounded cost that scales with set size. Developers should keep in mind that
* using it may render the function uncallable if the set grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function _clear(Set storage set) private {
uint256 len = _length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set, uint256 start, uint256 end) private view returns (bytes32[] memory) {
unchecked {
end = Math.min(end, _length(set));
start = Math.min(start, end);
uint256 len = end - start;
bytes32[] memory result = new bytes32[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(Bytes32Set storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(AddressSet storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set, uint256 start, uint256 end) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(UintSet storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set, uint256 start, uint256 end) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
struct StringSet {
// Storage of set values
string[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(string value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(StringSet storage set, string memory value) internal returns (bool) {
if (!contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(StringSet storage set, string memory value) internal returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
string memory lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(StringSet storage set) internal {
uint256 len = length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(StringSet storage set, string memory value) internal view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(StringSet storage set) internal view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(StringSet storage set, uint256 index) internal view returns (string memory) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(StringSet storage set) internal view returns (string[] memory) {
return set._values;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(StringSet storage set, uint256 start, uint256 end) internal view returns (string[] memory) {
unchecked {
end = Math.min(end, length(set));
start = Math.min(start, end);
uint256 len = end - start;
string[] memory result = new string[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
struct BytesSet {
// Storage of set values
bytes[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(BytesSet storage set, bytes memory value) internal returns (bool) {
if (!contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(BytesSet storage set, bytes memory value) internal returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes memory lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(BytesSet storage set) internal {
uint256 len = length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(BytesSet storage set, bytes memory value) internal view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(BytesSet storage set) internal view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(BytesSet storage set, uint256 index) internal view returns (bytes memory) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(BytesSet storage set) internal view returns (bytes[] memory) {
return set._values;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(BytesSet storage set, uint256 start, uint256 end) internal view returns (bytes[] memory) {
unchecked {
end = Math.min(end, length(set));
start = Math.min(start, end);
uint256 len = end - start;
bytes[] memory result = new bytes[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./EntropyStructs.sol";
// Deprecated -- these events are still emitted, but the lack of indexing
// makes them hard to use.
interface EntropyEvents {
event Registered(EntropyStructs.ProviderInfo provider);
event Requested(EntropyStructs.Request request);
event RequestedWithCallback(
address indexed provider,
address indexed requestor,
uint64 indexed sequenceNumber,
bytes32 userRandomNumber,
EntropyStructs.Request request
);
event Revealed(
EntropyStructs.Request request,
bytes32 userRevelation,
bytes32 providerRevelation,
bytes32 blockHash,
bytes32 randomNumber
);
event RevealedWithCallback(
EntropyStructs.Request request,
bytes32 userRandomNumber,
bytes32 providerRevelation,
bytes32 randomNumber
);
event CallbackFailed(
address indexed provider,
address indexed requestor,
uint64 indexed sequenceNumber,
bytes32 userRandomNumber,
bytes32 providerRevelation,
bytes32 randomNumber,
bytes errorCode
);
event ProviderFeeUpdated(address provider, uint128 oldFee, uint128 newFee);
event ProviderDefaultGasLimitUpdated(
address indexed provider,
uint32 oldDefaultGasLimit,
uint32 newDefaultGasLimit
);
event ProviderUriUpdated(address provider, bytes oldUri, bytes newUri);
event ProviderFeeManagerUpdated(
address provider,
address oldFeeManager,
address newFeeManager
);
event ProviderMaxNumHashesAdvanced(
address provider,
uint32 oldMaxNumHashes,
uint32 newMaxNumHashes
);
event Withdrawal(
address provider,
address recipient,
uint128 withdrawnAmount
);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./EntropyStructs.sol";
/**
* @title EntropyEventsV2
* @notice Interface defining events for the Entropy V2 system, which handles random number generation
* and provider management on Ethereum.
* @dev This interface is used to emit events that track the lifecycle of random number requests,
* provider registrations, and system configurations.
*/
interface EntropyEventsV2 {
/**
* @notice Emitted when a new provider registers with the Entropy system
* @param provider The address of the registered provider
* @param extraArgs A field for extra data for forward compatibility.
*/
event Registered(address indexed provider, bytes extraArgs);
/**
* @notice Emitted when a user requests a random number from a provider
* @param provider The address of the provider handling the request
* @param caller The address of the user requesting the random number
* @param sequenceNumber A unique identifier for this request
* @param userContribution The user's contribution to the random number
* @param gasLimit The gas limit for the callback.
* @param extraArgs A field for extra data for forward compatibility.
*/
event Requested(
address indexed provider,
address indexed caller,
uint64 indexed sequenceNumber,
bytes32 userContribution,
uint32 gasLimit,
bytes extraArgs
);
/**
* @notice Emitted when a provider reveals the generated random number
* @param provider The address of the provider that generated the random number
* @param caller The address of the user who requested the random number (and who receives a callback)
* @param sequenceNumber The unique identifier of the request
* @param randomNumber The generated random number
* @param userContribution The user's contribution to the random number
* @param providerContribution The provider's contribution to the random number
* @param callbackFailed Whether the callback to the caller failed
* @param callbackReturnValue Return value from the callback. If the callback failed, this field contains
* the error code and any additional returned data. Note that "" often indicates an out-of-gas error.
* If the callback returns more than 256 bytes, only the first 256 bytes of the callback return value are included.
* @param callbackGasUsed How much gas the callback used.
* @param extraArgs A field for extra data for forward compatibility.
*/
event Revealed(
address indexed provider,
address indexed caller,
uint64 indexed sequenceNumber,
bytes32 randomNumber,
bytes32 userContribution,
bytes32 providerContribution,
bool callbackFailed,
bytes callbackReturnValue,
uint32 callbackGasUsed,
bytes extraArgs
);
/**
* @notice Emitted when a provider updates their fee
* @param provider The address of the provider updating their fee
* @param oldFee The previous fee amount
* @param newFee The new fee amount
* @param extraArgs A field for extra data for forward compatibility.
*/
event ProviderFeeUpdated(
address indexed provider,
uint128 oldFee,
uint128 newFee,
bytes extraArgs
);
/**
* @notice Emitted when a provider updates their default gas limit
* @param provider The address of the provider updating their gas limit
* @param oldDefaultGasLimit The previous default gas limit
* @param newDefaultGasLimit The new default gas limit
* @param extraArgs A field for extra data for forward compatibility.
*/
event ProviderDefaultGasLimitUpdated(
address indexed provider,
uint32 oldDefaultGasLimit,
uint32 newDefaultGasLimit,
bytes extraArgs
);
/**
* @notice Emitted when a provider updates their URI
* @param provider The address of the provider updating their URI
* @param oldUri The previous URI
* @param newUri The new URI
* @param extraArgs A field for extra data for forward compatibility.
*/
event ProviderUriUpdated(
address indexed provider,
bytes oldUri,
bytes newUri,
bytes extraArgs
);
/**
* @notice Emitted when a provider updates their fee manager address
* @param provider The address of the provider updating their fee manager
* @param oldFeeManager The previous fee manager address
* @param newFeeManager The new fee manager address
* @param extraArgs A field for extra data for forward compatibility.
*/
event ProviderFeeManagerUpdated(
address indexed provider,
address oldFeeManager,
address newFeeManager,
bytes extraArgs
);
/**
* @notice Emitted when a provider updates their maximum number of hashes that can be advanced
* @param provider The address of the provider updating their max hashes
* @param oldMaxNumHashes The previous maximum number of hashes
* @param newMaxNumHashes The new maximum number of hashes
* @param extraArgs A field for extra data for forward compatibility.
*/
event ProviderMaxNumHashesAdvanced(
address indexed provider,
uint32 oldMaxNumHashes,
uint32 newMaxNumHashes,
bytes extraArgs
);
/**
* @notice Emitted when a provider withdraws their accumulated fees
* @param provider The address of the provider withdrawing fees
* @param recipient The address receiving the withdrawn fees
* @param withdrawnAmount The amount of fees withdrawn
* @param extraArgs A field for extra data for forward compatibility.
*/
event Withdrawal(
address indexed provider,
address indexed recipient,
uint128 withdrawnAmount,
bytes extraArgs
);
}// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
// This contract holds old versions of the Entropy structs that are no longer used for contract storage.
// However, they are still used in EntropyEvents to maintain the public interface of prior versions of
// the Entropy contract.
//
// See EntropyStructsV2 for the struct definitions currently in use.
contract EntropyStructs {
struct ProviderInfo {
uint128 feeInWei;
uint128 accruedFeesInWei;
// The commitment that the provider posted to the blockchain, and the sequence number
// where they committed to this. This value is not advanced after the provider commits,
// and instead is stored to help providers track where they are in the hash chain.
bytes32 originalCommitment;
uint64 originalCommitmentSequenceNumber;
// Metadata for the current commitment. Providers may optionally use this field to help
// manage rotations (i.e., to pick the sequence number from the correct hash chain).
bytes commitmentMetadata;
// Optional URI where clients can retrieve revelations for the provider.
// Client SDKs can use this field to automatically determine how to retrieve random values for each provider.
// TODO: specify the API that must be implemented at this URI
bytes uri;
// The first sequence number that is *not* included in the current commitment (i.e., an exclusive end index).
// The contract maintains the invariant that sequenceNumber <= endSequenceNumber.
// If sequenceNumber == endSequenceNumber, the provider must rotate their commitment to add additional random values.
uint64 endSequenceNumber;
// The sequence number that will be assigned to the next inbound user request.
uint64 sequenceNumber;
// The current commitment represents an index/value in the provider's hash chain.
// These values are used to verify requests for future sequence numbers. Note that
// currentCommitmentSequenceNumber < sequenceNumber.
//
// The currentCommitment advances forward through the provider's hash chain as values
// are revealed on-chain.
bytes32 currentCommitment;
uint64 currentCommitmentSequenceNumber;
// An address that is authorized to set / withdraw fees on behalf of this provider.
address feeManager;
// Maximum number of hashes to record in a request. This should be set according to the maximum gas limit
// the provider supports for callbacks.
uint32 maxNumHashes;
}
struct Request {
// Storage slot 1 //
address provider;
uint64 sequenceNumber;
// The number of hashes required to verify the provider revelation.
uint32 numHashes;
// Storage slot 2 //
// The commitment is keccak256(userCommitment, providerCommitment). Storing the hash instead of both saves 20k gas by
// eliminating 1 store.
bytes32 commitment;
// Storage slot 3 //
// The number of the block where this request was created.
// Note that we're using a uint64 such that we have an additional space for an address and other fields in
// this storage slot. Although block.number returns a uint256, 64 bits should be plenty to index all of the
// blocks ever generated.
uint64 blockNumber;
// The address that requested this random number.
address requester;
// If true, incorporate the blockhash of blockNumber into the generated random value.
bool useBlockhash;
// True if this is a request that expects a callback.
bool isRequestWithCallback;
}
}// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
contract EntropyStructsV2 {
struct ProviderInfo {
uint128 feeInWei;
uint128 accruedFeesInWei;
// The commitment that the provider posted to the blockchain, and the sequence number
// where they committed to this. This value is not advanced after the provider commits,
// and instead is stored to help providers track where they are in the hash chain.
bytes32 originalCommitment;
uint64 originalCommitmentSequenceNumber;
// Metadata for the current commitment. Providers may optionally use this field to help
// manage rotations (i.e., to pick the sequence number from the correct hash chain).
bytes commitmentMetadata;
// Optional URI where clients can retrieve revelations for the provider.
// Client SDKs can use this field to automatically determine how to retrieve random values for each provider.
// TODO: specify the API that must be implemented at this URI
bytes uri;
// The first sequence number that is *not* included in the current commitment (i.e., an exclusive end index).
// The contract maintains the invariant that sequenceNumber <= endSequenceNumber.
// If sequenceNumber == endSequenceNumber, the provider must rotate their commitment to add additional random values.
uint64 endSequenceNumber;
// The sequence number that will be assigned to the next inbound user request.
uint64 sequenceNumber;
// The current commitment represents an index/value in the provider's hash chain.
// These values are used to verify requests for future sequence numbers. Note that
// currentCommitmentSequenceNumber < sequenceNumber.
//
// The currentCommitment advances forward through the provider's hash chain as values
// are revealed on-chain.
bytes32 currentCommitment;
uint64 currentCommitmentSequenceNumber;
// An address that is authorized to set / withdraw fees on behalf of this provider.
address feeManager;
// Maximum number of hashes to record in a request. This should be set according to the maximum gas limit
// the provider supports for callbacks.
uint32 maxNumHashes;
// Default gas limit to use for callbacks.
uint32 defaultGasLimit;
}
struct Request {
// Storage slot 1 //
address provider;
uint64 sequenceNumber;
// The number of hashes required to verify the provider revelation.
uint32 numHashes;
// Storage slot 2 //
// The commitment is keccak256(userCommitment, providerCommitment). Storing the hash instead of both saves 20k gas by
// eliminating 1 store.
bytes32 commitment;
// Storage slot 3 //
// The number of the block where this request was created.
// Note that we're using a uint64 such that we have an additional space for an address and other fields in
// this storage slot. Although block.number returns a uint256, 64 bits should be plenty to index all of the
// blocks ever generated.
uint64 blockNumber;
// The address that requested this random number.
address requester;
// If true, incorporate the blockhash of blockNumber into the generated random value.
bool useBlockhash;
// Status flag for requests with callbacks. See EntropyConstants for the possible values of this flag.
uint8 callbackStatus;
// The gasLimit in units of 10k gas. (i.e., 2 = 20k gas). We're using units of 10k in order to fit this
// field into the remaining 2 bytes of this storage slot. The dynamic range here is 10k - 655M, which should
// cover all real-world use cases.
uint16 gasLimit10k;
}
}// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
abstract contract IEntropyConsumer {
// This method is called by Entropy to provide the random number to the consumer.
// It asserts that the msg.sender is the Entropy contract. It is not meant to be
// override by the consumer.
function _entropyCallback(
uint64 sequence,
address provider,
bytes32 randomNumber
) external {
address entropy = getEntropy();
require(entropy != address(0), "Entropy address not set");
require(msg.sender == entropy, "Only Entropy can call this function");
entropyCallback(sequence, provider, randomNumber);
}
// getEntropy returns Entropy contract address. The method is being used to check that the
// callback is indeed from Entropy contract. The consumer is expected to implement this method.
// Entropy address can be found here - https://docs.pyth.network/entropy/contract-addresses
function getEntropy() internal view virtual returns (address);
// This method is expected to be implemented by the consumer to handle the random number.
// It will be called by _entropyCallback after _entropyCallback ensures that the call is
// indeed from Entropy contract.
function entropyCallback(
uint64 sequence,
address provider,
bytes32 randomNumber
) internal virtual;
}// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "./EntropyEvents.sol";
import "./EntropyEventsV2.sol";
import "./EntropyStructsV2.sol";
interface IEntropyV2 is EntropyEventsV2 {
/// @notice Request a random number using the default provider with default gas limit
/// @return assignedSequenceNumber A unique identifier for this request
/// @dev The address calling this function should be a contract that inherits from the IEntropyConsumer interface.
/// The `entropyCallback` method on that interface will receive a callback with the returned sequence number and
/// the generated random number.
///
/// `entropyCallback` will be run with the provider's configured default gas limit.
///
/// This method will revert unless the caller provides a sufficient fee (at least `getFeeV2()`) as msg.value.
/// Note that the fee can change over time. Callers of this method should explicitly compute `getFeeV2()`
/// prior to each invocation (as opposed to hardcoding a value). Further note that excess value is *not* refunded to the caller.
///
/// Note that this method uses an in-contract PRNG to generate the user's contribution to the random number.
/// This approach modifies the security guarantees such that a dishonest validator and provider can
/// collude to manipulate the result (as opposed to a malicious user and provider). That is, the user
/// now trusts the validator honestly draw a random number. If you wish to avoid this trust assumption,
/// call a variant of `requestV2` that accepts a `userRandomNumber` parameter.
function requestV2()
external
payable
returns (uint64 assignedSequenceNumber);
/// @notice Request a random number using the default provider with specified gas limit
/// @param gasLimit The gas limit for the callback function.
/// @return assignedSequenceNumber A unique identifier for this request
/// @dev The address calling this function should be a contract that inherits from the IEntropyConsumer interface.
/// The `entropyCallback` method on that interface will receive a callback with the returned sequence number and
/// the generated random number.
///
/// `entropyCallback` will be run with the `gasLimit` provided to this function.
/// The `gasLimit` will be rounded up to a multiple of 10k (e.g., 19000 -> 20000), and furthermore is lower bounded
/// by the provider's configured default limit.
///
/// This method will revert unless the caller provides a sufficient fee (at least `getFeeV2(gasLimit)`) as msg.value.
/// Note that the fee can change over time. Callers of this method should explicitly compute `getFeeV2(gasLimit)`
/// prior to each invocation (as opposed to hardcoding a value). Further note that excess value is *not* refunded to the caller.
///
/// Note that this method uses an in-contract PRNG to generate the user's contribution to the random number.
/// This approach modifies the security guarantees such that a dishonest validator and provider can
/// collude to manipulate the result (as opposed to a malicious user and provider). That is, the user
/// now trusts the validator honestly draw a random number. If you wish to avoid this trust assumption,
/// call a variant of `requestV2` that accepts a `userRandomNumber` parameter.
function requestV2(
uint32 gasLimit
) external payable returns (uint64 assignedSequenceNumber);
/// @notice Request a random number from a specific provider with specified gas limit
/// @param provider The address of the provider to request from
/// @param gasLimit The gas limit for the callback function
/// @return assignedSequenceNumber A unique identifier for this request
/// @dev The address calling this function should be a contract that inherits from the IEntropyConsumer interface.
/// The `entropyCallback` method on that interface will receive a callback with the returned sequence number and
/// the generated random number.
///
/// `entropyCallback` will be run with the `gasLimit` provided to this function.
/// The `gasLimit` will be rounded up to a multiple of 10k (e.g., 19000 -> 20000), and furthermore is lower bounded
/// by the provider's configured default limit.
///
/// This method will revert unless the caller provides a sufficient fee (at least `getFeeV2(provider, gasLimit)`) as msg.value.
/// Note that provider fees can change over time. Callers of this method should explicitly compute `getFeeV2(provider, gasLimit)`
/// prior to each invocation (as opposed to hardcoding a value). Further note that excess value is *not* refunded to the caller.
///
/// Note that this method uses an in-contract PRNG to generate the user's contribution to the random number.
/// This approach modifies the security guarantees such that a dishonest validator and provider can
/// collude to manipulate the result (as opposed to a malicious user and provider). That is, the user
/// now trusts the validator honestly draw a random number. If you wish to avoid this trust assumption,
/// call a variant of `requestV2` that accepts a `userRandomNumber` parameter.
function requestV2(
address provider,
uint32 gasLimit
) external payable returns (uint64 assignedSequenceNumber);
/// @notice Request a random number from a specific provider with a user-provided random number and gas limit
/// @param provider The address of the provider to request from
/// @param userRandomNumber A random number provided by the user for additional entropy
/// @param gasLimit The gas limit for the callback function. Pass 0 to get a sane default value -- see note below.
/// @return assignedSequenceNumber A unique identifier for this request
/// @dev The address calling this function should be a contract that inherits from the IEntropyConsumer interface.
/// The `entropyCallback` method on that interface will receive a callback with the returned sequence number and
/// the generated random number.
///
/// `entropyCallback` will be run with the `gasLimit` provided to this function.
/// The `gasLimit` will be rounded up to a multiple of 10k (e.g., 19000 -> 20000), and furthermore is lower bounded
/// by the provider's configured default limit.
///
/// This method will revert unless the caller provides a sufficient fee (at least `getFeeV2(provider, gasLimit)`) as msg.value.
/// Note that provider fees can change over time. Callers of this method should explicitly compute `getFeeV2(provider, gasLimit)`
/// prior to each invocation (as opposed to hardcoding a value). Further note that excess value is *not* refunded to the caller.
function requestV2(
address provider,
bytes32 userRandomNumber,
uint32 gasLimit
) external payable returns (uint64 assignedSequenceNumber);
/// @notice Get information about a specific entropy provider
/// @param provider The address of the provider to query
/// @return info The provider information including configuration, fees, and operational status
/// @dev This method returns detailed information about a provider's configuration and capabilities.
/// The returned ProviderInfo struct contains information such as the provider's fee structure and gas limits.
function getProviderInfoV2(
address provider
) external view returns (EntropyStructsV2.ProviderInfo memory info);
/// @notice Get the address of the default entropy provider
/// @return provider The address of the default provider
/// @dev This method returns the address of the provider that will be used when no specific provider is specified
/// in the requestV2 calls. The default provider can be used to get the base fee and gas limit information.
function getDefaultProvider() external view returns (address provider);
/// @notice Get information about a specific request
/// @param provider The address of the provider that handled the request
/// @param sequenceNumber The unique identifier of the request
/// @return req The request information including status, random number, and other metadata
/// @dev This method allows querying the state of a previously made request. The returned Request struct
/// contains information about whether the request was fulfilled, the generated random number (if available),
/// and other metadata about the request.
function getRequestV2(
address provider,
uint64 sequenceNumber
) external view returns (EntropyStructsV2.Request memory req);
/// @notice Get the fee charged by the default provider for the default gas limit
/// @return feeAmount The fee amount in wei
/// @dev This method returns the base fee required to make a request using the default provider with
/// the default gas limit. This fee should be passed as msg.value when calling requestV2().
/// The fee can change over time, so this method should be called before each request.
function getFeeV2() external view returns (uint128 feeAmount);
/// @notice Get the fee charged by the default provider for a specific gas limit
/// @param gasLimit The gas limit for the callback function
/// @return feeAmount The fee amount in wei
/// @dev This method returns the fee required to make a request using the default provider with
/// the specified gas limit. This fee should be passed as msg.value when calling requestV2(gasLimit).
/// The fee can change over time, so this method should be called before each request.
function getFeeV2(
uint32 gasLimit
) external view returns (uint128 feeAmount);
/// @notice Get the fee charged by a specific provider for a request with a given gas limit
/// @param provider The address of the provider to query
/// @param gasLimit The gas limit for the callback function
/// @return feeAmount The fee amount in wei
/// @dev This method returns the fee required to make a request using the specified provider with
/// the given gas limit. This fee should be passed as msg.value when calling requestV2(provider, gasLimit)
/// or requestV2(provider, userRandomNumber, gasLimit). The fee can change over time, so this method
/// should be called before each request.
function getFeeV2(
address provider,
uint32 gasLimit
) external view returns (uint128 feeAmount);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
/**
* @title AccessControlDefaultAdminRulesEnumerable.sol.
*
* version 0.1.0
*
* @author abraxas https://abraxaslabs.io
*
* @dev AccessControl: This contract imports AccessControlDefaultAdminRules directly from
* OpenZeppelin. Within this contract we implement some of the functionality of OpenZeppelin's
* AccessControlEnumerable directly, allowing the clean inheritance of Enumeration -> default rule ->
* base contract class, therefore avoiding the need to override base class methods.
*/
import { AccessControlDefaultAdminRules } from "@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
contract AccessControlDefaultAdminRulesEnumerable is
AccessControlDefaultAdminRules
{
using EnumerableSet for EnumerableSet.AddressSet;
/**
* @dev This functionality adds a helpful enumeration over role types, allowing easy
* viewing of all addresses for a given role.
* Copied directly from:
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/extensions/AccessControlEnumerable.sol
* but WITHOUT the getRoleMemberCount and getRoleMember functions. These are not needed as we
* don't anticipate huge numbers of admins and only really want the convenience of the getRoleMembers function.
*/
// Mapping from role to role members, allowing for easy and transparent listing of role members.
// This is used in the AccessControlEnumerable implementation.
mapping(bytes32 role => EnumerableSet.AddressSet) private _roleMembers;
/**
* @param initialDelay_ The initial delay for the timelock.
* @param initialDefaultAdmin_ The initial default admin address.
*/
constructor(
uint48 initialDelay_,
address initialDefaultAdmin_
) AccessControlDefaultAdminRules(initialDelay_, initialDefaultAdmin_) {}
/**
* @dev Return all accounts that have `role`
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* un-callable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function getRoleMembers(bytes32 role) public view returns (address[] memory) {
return _roleMembers[role].values();
}
/**
* @dev Overload {AccessControl-_grantRole} to track enumerable memberships
*/
function _grantRole(
bytes32 role,
address account
) internal override returns (bool) {
bool granted = super._grantRole(role, account);
if (granted) {
_roleMembers[role].add(account);
}
return granted;
}
/**
* @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
*/
function _revokeRole(
bytes32 role,
address account
) internal override returns (bool) {
bool revoked = super._revokeRole(role, account);
if (revoked) {
_roleMembers[role].remove(account);
}
return revoked;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
interface IWrappedNativeToken {
function deposit() external payable;
function withdraw(uint256 wad) external;
function totalSupply() external view returns (uint256);
function approve(address guy, uint256 wad) external returns (bool);
function transfer(address dst, uint256 wad) external returns (bool);
function transferFrom(
address src,
address dst,
uint256 wad
) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
/**
* @title StrideFeistelSetGenerator.sol
*
* version 0.1.0
*
* @author abraxas https://abraxaslabs.io
*
* @dev Combines coprime stride with Feistel network permutation.
* - Uses coprime stride to generate scattered input indices
* - Excludes step=1 to prevent sequential inputs
* - Applies Feistel permutation to those scattered inputs
* - Reduces the clustering of feistel alone
* - Gas efficient: ~360 gas per winner + ~200 one-time coprime search
*/
contract StrideFeistelSetGenerator {
uint256 private constant DEFAULT_ROUNDS = 6;
uint256 private constant MAX_ROUNDS = 12;
// Safety bounds for cycle-walking. We wouldn't reasonably expect to hit this within
// the lifetime of the universe assuming continuous runs.
uint256 private constant MAX_CYCLE_WALKS = 128;
/**
* @dev generateSet Returns a full set of scattered permutation over [0..maxValue]
* @param maxValue The maximum value (inclusive)
* @param setSize The size of the set to generate
* @param seed The random seed
* @param rounds The number of Feistel rounds (0 => default(6))
* @return out The scattered permutation over [0..maxValue]
*/
function generateSet(
uint256 maxValue, // inclusive
uint256 setSize,
bytes32 seed,
uint256 rounds // 0 => default(6)
) external pure returns (uint32[] memory) {
return generateSlice(maxValue, 0, setSize, seed, rounds);
}
/**
* @dev generateSlice Returns a scattered permutation slice over [0..maxValue]
* @param maxValue The maximum value (inclusive)
* @param startIndex The starting index of the slice
* @param count The number of values to generate
* @param seed The random seed
* @param rounds The number of Feistel rounds (0 => default(6))
* @return out The scattered permutation slice over [0..maxValue]
*/
function generateSlice(
uint256 maxValue, // inclusive
uint256 startIndex, // which winner to start from (0..setSize-1)
uint256 count, // how many winners to generate
bytes32 seed,
uint256 rounds // 0 => default(6)
) public pure returns (uint32[] memory out) {
if (count == 0) {
revert("count=0");
}
if (maxValue > type(uint32).max) {
revert("max too high");
}
uint256 N = maxValue + 1; // domain size
if (startIndex >= N) {
revert("startIndex OOB");
}
if (startIndex + count > N) {
revert("slice exceeds domain");
}
out = new uint32[](count);
// Derive stride parameters (offset and step)
bytes32 strideSeed = keccak256(abi.encodePacked(seed, "STRIDE"));
uint256 offset = _deriveOffset(strideSeed, N);
uint256 step = _findCoprimeExcludingOne(strideSeed, N);
unchecked {
for (uint256 i = 0; i < count; ++i) {
// Calculate stride index (scattered input)
uint256 strideIndex = addmod(
offset,
mulmod(startIndex + i, step, N),
N
);
// Apply Feistel permutation to the stride index
uint256 v = _permuteInRangeSafe(strideIndex, N, seed, rounds);
out[i] = uint32(v);
}
}
}
/**
* @dev _deriveOffset Derives a uniform random offset in [0..N)
* @param strideSeed The seed for the stride
* @param N The domain size
* @return The offset
*/
function _deriveOffset(
bytes32 strideSeed,
uint256 N
) private pure returns (uint256) {
bytes32 offsetSeed = keccak256(abi.encodePacked(strideSeed, "OFFSET"));
return uint256(offsetSeed) % N;
}
/**
* @dev _findCoprimeExcludingOne Finds a coprime step in [2..N-1], explicitly excluding step=1
* @param strideSeed The seed for the stride
* @param N The domain size
* @return candidate_ The coprime step
*/
function _findCoprimeExcludingOne(
bytes32 strideSeed,
uint256 N
) private pure returns (uint256 candidate_) {
if (N <= 2) {
return 1; // Edge case: no choice but step=1
}
bytes32 stepSeed = keccak256(abi.encodePacked(strideSeed, "STEP"));
unchecked {
while (true) {
// Generate candidate in [1..N-1]
candidate_ = (uint256(stepSeed) % (N - 1)) + 1;
// Exclude step=1 to prevent sequential inputs
if (candidate_ == 1) {
stepSeed = keccak256(abi.encodePacked(stepSeed, "RETRY"));
continue;
}
// Check if coprime (using Euclidean GCD)
// Quick optimization: if N is even, only odd steps can be coprime
if ((N & 1) == 1 || (candidate_ & 1) == 1) {
if (_gcd(N, candidate_) == 1) {
return candidate_;
}
}
stepSeed = keccak256(abi.encodePacked(stepSeed, "RETRY"));
}
}
}
/**
* @dev _gcd Euclidean GCD algorithm
* @param a The first number
* @param b The second number
* @return The GCD
*/
function _gcd(uint256 a, uint256 b) private pure returns (uint256) {
unchecked {
while (b != 0) {
uint256 temp = b;
b = a % b;
a = temp;
}
return a;
}
}
/**
* @dev _permuteInRangeSafe Permutes an index value into the range [0..N)
* @param index The input index
* @param N The domain size
* @param seed The random seed
* @param rounds The number of Feistel rounds
* @return The permuted value in [0..N) or uint256.max if no valid output found
*/
function _permuteInRangeSafe(
uint256 index,
uint256 N,
bytes32 seed,
uint256 rounds
) private pure returns (uint256) {
if (N == 0) {
revert("N=0");
}
if (N == 1) {
return 0;
}
uint256 r = rounds;
if (r == 0) {
r = DEFAULT_ROUNDS;
}
if (r > MAX_ROUNDS) {
r = MAX_ROUNDS;
}
uint256 k = _ceilLog2(N);
uint256 kMask = (uint256(1) << k) - 1;
// Single seed, high attempt limit
uint256 cur = index;
unchecked {
for (uint256 attempts = 0; attempts < MAX_CYCLE_WALKS; ++attempts) {
uint256 inK = cur & kMask;
uint256 y = _feistelOverPow2(inK, k, seed, r);
if (y < N) {
return y;
}
cur = y; // cycle-walk
}
}
// If we get here, return special "no winner" value
return type(uint256).max;
}
/**
* @dev _ceilLog2 Calculates the ceiling of log2(x)
* @param x The input value
* @return bits The ceiling of log2(x)
*/
function _ceilLog2(uint256 x) private pure returns (uint256 bits) {
unchecked {
uint256 v = x - 1;
while (v > 0) {
++bits;
v >>= 1;
}
}
}
/**
* @dev _feistelOverPow2 Feistel network over power-of-2 domain
* @param inputValue The input value
* @param numBits The number of bits in the domain
* @param seed The random seed
* @param roundCount The number of Feistel rounds
* @return The permuted value
*/
function _feistelOverPow2(
uint256 inputValue,
uint256 numBits,
bytes32 seed,
uint256 roundCount
) private pure returns (uint256) {
// Split k bits: left gets ceil(k/2), right gets floor(k/2).
uint256 rightBits = numBits / 2;
uint256 leftBits = numBits - rightBits;
uint256 rightMask = (uint256(1) << rightBits) - 1;
uint256 leftMask = (uint256(1) << leftBits) - 1;
// Extract initial halves (R = low bits, L = high bits)
uint256 R = inputValue & rightMask;
uint256 L = (inputValue >> rightBits) & leftMask;
// Track the "current" masks/widths (they swap each round)
uint256 curRMask = rightMask;
uint256 curLMask = leftMask;
uint256 curRBits = rightBits; // number of bits of the current R half
unchecked {
for (uint256 r = 0; r < roundCount; ++r) {
// Ensure halves are within their current widths
R &= curRMask;
L &= curLMask;
// F(R): truncate to CURRENT L size (the XOR target)
bytes32 h = keccak256(abi.encodePacked(seed, uint8(r), R));
uint256 F = uint256(h) & curLMask;
// (L, R) <- (R, (L ^ F) masked to current L width)
uint256 newR = (L ^ F) & curLMask;
uint256 newL = R; // will take the previous R width
// Swap halves and their masks/widths for next round
L = newL;
R = newR;
// Swap masks
uint256 tmpMask = curLMask;
curLMask = curRMask;
curRMask = tmpMask;
// Swap bit-counts (for recomposition later if odd rounds)
uint256 tmpBits;
if (curRBits == rightBits) {
tmpBits = leftBits;
} else {
tmpBits = rightBits;
}
curRBits = tmpBits;
}
}
// After 'roundCount' rounds, R currently has 'curRBits' width; L has the other.
// Recombine as: (L << curRBits) | (R & curRMask)
return (L << curRBits) | (R & curRMask);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
/**
* @title TimeLock.sol.
*
* version 0.1.0
*
* @author abraxas https://abraxaslabs.io
*
* @dev TimeLock implements a locking mechanism that will automatically expire after a set period of time.
* An internal method to manually remove the lock is also provided. This is intended to be called by the
* implementing contract either when processing is complete or when an admin manually removes the lock.
*
* An expiring lock ensures that certain actions are not blocked forever in the event of a failed process
* or lack of admin intervention.
*/
contract TimeLock {
// maxLockSeconds. This determines the maximum number of seconds that the lock will be active for.
// As a safety feature the lock can only be active for a maximum time period.
// After this time has elapsed the lock is automatically lifted. This allows contracts to implement
// a triple fallback for the lock being lifted:
// 1. At the completion of associated processing the lock is lifted.
// 2. If for any reason the process is not completed within the maxLockSeconds the lock
// is lifted automatically.
// 3. Implementing contracts can include an admin method to call _unlock to manually lift the lock.
uint256 internal immutable maxLockSeconds;
// minLockGapSeconds. This is the minimum number of seconds that must pass between
// the last lock being applied and a new lock being applied. As draws involve a lock being
// placed on deposits and withdrawals this parameter is also validated when scheduling raffles
// to ensure that a new raffle will not complete within minLockGapSeconds of any existing raffle.
uint256 internal immutable minLockGapSeconds;
// unlockAtTimestamp. This stores the timestamp that withdrawals and deposits
// will be locked before. If the block.timestamp is lower than this timestamp deposits and
// withdrawals are locked. If the block.timestamp is after this timestamp then deposits
// and withdrawals can be made.
uint256 internal unlockAtTimestamp;
/**
* @param maxLockSeconds_ The maximum number of seconds that a lock can be applied.
* @param minLockGapSeconds_ The minimum number of seconds that must pass between
* the last lock being applied and a new lock being applied.
*/
constructor(uint256 maxLockSeconds_, uint256 minLockGapSeconds_) {
maxLockSeconds = maxLockSeconds_;
minLockGapSeconds = minLockGapSeconds_;
}
modifier onlyWhenUnlocked() {
if (_isLocked()) {
revert("Locked");
}
_;
}
function _isLocked() internal view returns (bool isLocked_) {
return (block.timestamp < unlockAtTimestamp);
}
/**
* @dev _timelock Turns the lock ON for the specified time period.
*/
function _timelock() internal {
// We CANNOT set the lock ON if it was active within the minimum gap. This ensures that there
// is always a minimum time gap between lock states and an endless sequence of time locks
// cannot be enacted.
if (block.timestamp < (unlockAtTimestamp + minLockGapSeconds)) {
revert("Cannot lock at this time");
}
// No one can decide the lock period, it is always the maxLockSeconds:
unlockAtTimestamp = block.timestamp + maxLockSeconds;
}
/**
* @dev _unlock Manually unlocks by setting the unlockAtTimestamp to block.timestamp.
*/
function _unlock() internal {
unlockAtTimestamp = block.timestamp;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
/**
* @title UnstoppableTransfer.sol.
*
* version 0.1.0
*
* @author abraxas https://abraxaslabs.io
*
* @dev UnstoppableTransfer is a utility contract for the sending of native token to another recipient.
* In the event of a failure to send native token the contract wraps the native token and delivers this
* ERC20 amount to the recipient. This is a useful utility for contracts that need to make a native token
* payment as part of processing and *cannot* have that transfer fail, for example through a contract
* recipient that reverts or uses the entire gas stipend of a call.
*/
import { IWrappedNativeToken } from "./IWrappedNativeToken.sol";
import {
ExcessivelySafeCall
} from "@nomad-xyz/excessively-safe-call/src/ExcessivelySafeCall.sol";
contract UnstoppableTransfer {
using ExcessivelySafeCall for address;
// Minimum gas amount to use for transfers. This protects against a caller passing 0, maybe not
// understanding what this argument is for, and therefore ALWAYS receiving wrapped tokens
// instead of native tokens. We allow callers to pass in their own max gas amount as it protects
// against chain changes that might result in a higher gas limit being required for transfers.
uint256 private constant MIN_GAS_AMOUNT = 100_000;
IWrappedNativeToken public immutable wrappedNativeToken;
/**
* @param wrappedNativeToken_ The address of the wrapped native token contract.
*/
constructor(address wrappedNativeToken_) {
wrappedNativeToken = IWrappedNativeToken(wrappedNativeToken_);
}
/**
* @dev _unstoppableTransfer Internal method to handle the transfer of native tokens to a recipient.
* @param recipient_ The address of the recipient of the native tokens.
* @param amount_ The amount of native tokens to transfer.
* @param maxGas_ The maximum gas to use for the transfer.
*/
function _unstoppableTransfer(
address recipient_,
uint256 amount_,
uint256 maxGas_
) internal {
// Set the gas for the call to the maximum of the caller's input or the minimum gas amount.
uint256 gasForCall = maxGas_;
if (gasForCall < MIN_GAS_AMOUNT) {
gasForCall = MIN_GAS_AMOUNT;
}
// Try to send the native token, use ExcessivelySafeCall to prevent large amounts of copied return data.
(bool success, ) = recipient_.excessivelySafeCall(
gasForCall,
amount_,
uint16(0),
""
);
if (!success) {
// Wrap the token:
wrappedNativeToken.deposit{ value: amount_ }();
// Send it:
wrappedNativeToken.transfer(recipient_, amount_);
}
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"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":"uint48","name":"initialDelay_","type":"uint48"},{"internalType":"address","name":"initialDefaultAdmin_","type":"address"},{"internalType":"address[]","name":"platformAdmins_","type":"address[]"},{"internalType":"address[]","name":"raffleAdmins_","type":"address[]"},{"internalType":"address[]","name":"batchOperators_","type":"address[]"},{"internalType":"uint256","name":"minimumBalance_","type":"uint256"},{"internalType":"address","name":"nftTreasury_","type":"address"},{"internalType":"address","name":"platformFeeTreasury_","type":"address"},{"internalType":"uint256","name":"platformFeePercentage_","type":"uint256"},{"internalType":"address","name":"entropyAddress_","type":"address"},{"internalType":"uint256","name":"minOpenPeriodSeconds_","type":"uint256"},{"internalType":"address","name":"wrappedNativeToken_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"uint48","name":"schedule","type":"uint48"}],"name":"AccessControlEnforcedDefaultAdminDelay","type":"error"},{"inputs":[],"name":"AccessControlEnforcedDefaultAdminRules","type":"error"},{"inputs":[{"internalType":"address","name":"defaultAdmin","type":"address"}],"name":"AccessControlInvalidDefaultAdmin","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"bytes32","name":"key","type":"bytes32"}],"name":"EnumerableMapNonexistentKey","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"uint8","name":"bits","type":"uint8"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"SafeCastOverflowedUintDowncast","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"AutoDistributionComplete","type":"event"},{"anonymous":false,"inputs":[],"name":"DefaultAdminDelayChangeCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"newDelay","type":"uint48"},{"indexed":false,"internalType":"uint48","name":"effectSchedule","type":"uint48"}],"name":"DefaultAdminDelayChangeScheduled","type":"event"},{"anonymous":false,"inputs":[],"name":"DefaultAdminTransferCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"},{"indexed":false,"internalType":"uint48","name":"acceptSchedule","type":"uint48"}],"name":"DefaultAdminTransferScheduled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"deposit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"balance","type":"uint256"},{"indexed":false,"internalType":"bool","name":"newUser","type":"bool"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"DepositsPausedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"DrawsPausedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"ERC721ManualTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"enum Minthub.RaffleStatus","name":"newStatus","type":"uint8"}],"name":"LogRaffleStatus","type":"event"},{"anonymous":false,"inputs":[],"name":"ManualUnlock","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMinimumBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMinimumBalance","type":"uint256"}],"name":"MinimumBalanceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldNftTreasury","type":"address"},{"indexed":false,"internalType":"address","name":"newNftTreasury","type":"address"}],"name":"NftTreasurySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"ticketIndex","type":"uint256"}],"name":"NoWinner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"enum Minthub.ParticipationListType","name":"listType","type":"uint8"},{"indexed":false,"internalType":"uint256[]","name":"raffleIds","type":"uint256[]"}],"name":"ParticipationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldPlatformFeePercentage","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPlatformFeePercentage","type":"uint256"}],"name":"PlatformFeePercentageSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPlatformFeeTreasury","type":"address"},{"indexed":false,"internalType":"address","name":"newPlatformFeeTreasury","type":"address"}],"name":"PlatformFeeTreasurySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"winner","type":"address"},{"indexed":false,"internalType":"uint256","name":"prizeIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ticketNumber","type":"uint256"}],"name":"PrizeAwarded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"prizeIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"winner","type":"address"},{"indexed":false,"internalType":"uint256","name":"ticketNumber","type":"uint256"},{"indexed":false,"internalType":"enum Minthub.PrizeStatus","name":"newStatus","type":"uint8"}],"name":"PrizeStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"distributionCount","type":"uint256"}],"name":"PrizesDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"newRaffle","type":"bool"},{"indexed":false,"internalType":"uint256","name":"ticketPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"closedTimestamp","type":"uint256"},{"indexed":false,"internalType":"address","name":"nftAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenIdsCount","type":"uint256"},{"indexed":false,"internalType":"address","name":"projectTreasury","type":"address"},{"indexed":false,"internalType":"uint256","name":"raffleFeePercentage","type":"uint256"}],"name":"RaffleUpdated","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"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startTicketNumber","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"numberOfTickets","type":"uint256"}],"name":"TicketRangeEnumeratedForUser","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startUserIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"endUserIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalTickets","type":"uint256"},{"indexed":false,"internalType":"bool","name":"ticketEnumerationComplete","type":"bool"}],"name":"TicketsEnumerated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"distributedCount","type":"uint256"}],"name":"UserPrizesDistributedForRaffle","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"},{"indexed":false,"internalType":"uint32[]","name":"winningTickets","type":"uint32[]"}],"name":"WinningTicketsDrawnBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"Withdrawal","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"sequence","type":"uint64"},{"internalType":"address","name":"provider","type":"address"},{"internalType":"bytes32","name":"randomNumber","type":"bytes32"}],"name":"_entropyCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accumulatedPlatformFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"}],"name":"allPrizesAreOwnedByTreasury","outputs":[{"internalType":"bool","name":"allPrizesOwnedByTreasury_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"beginDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelDefaultAdminTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"}],"name":"cancelRaffle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"}],"name":"changeDefaultAdminDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"ticketPrice_","type":"uint256"},{"internalType":"uint256","name":"closedTimestamp_","type":"uint256"},{"internalType":"address","name":"nftAddress_","type":"address"},{"internalType":"uint16[]","name":"tokenIds_","type":"uint16[]"},{"internalType":"address","name":"projectTreasury_","type":"address"},{"internalType":"uint256","name":"raffleFeePercentage_","type":"uint256"},{"internalType":"bool","name":"validatePrizeOwnership_","type":"bool"}],"name":"createRaffle","outputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultAdminDelay","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultAdminDelayIncreaseWait","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"users_","type":"address[]"},{"internalType":"uint256","name":"depositPerUser_","type":"uint256"}],"name":"depositForUsers","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"}],"name":"depositOf","outputs":[{"internalType":"uint256","name":"balance_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"batchSize_","type":"uint256"},{"internalType":"uint256","name":"maxGasForNativeTokenTransfer_","type":"uint256"}],"name":"distributeAllPrizesForRaffleBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"batchSize_","type":"uint256"},{"internalType":"uint256","name":"startIndex_","type":"uint256"},{"internalType":"bool","name":"revertWhenNothingToDistribute_","type":"bool"},{"internalType":"uint256","name":"maxGasForNativeTokenTransfer_","type":"uint256"}],"name":"distributeAllPrizesForRaffleWithStartIndexBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"prizeIndex_","type":"uint256"},{"internalType":"uint256","name":"maxGasForNativeTokenTransfer_","type":"uint256"}],"name":"distributeSinglePrize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"uint256","name":"maxGasForNativeTokenTransfer_","type":"uint256"}],"name":"distributeUserPrizes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"maxGasForNativeTokenTransfer_","type":"uint256"}],"name":"distributeUserPrizesForRaffle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"batchSize_","type":"uint256"}],"name":"drawWinningTicketsBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"batchSize_","type":"uint256"}],"name":"enumerateTicketsBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex_","type":"uint256"},{"internalType":"uint256","name":"count_","type":"uint256"}],"name":"getConciseRaffles","outputs":[{"components":[{"internalType":"uint256","name":"raffleId","type":"uint256"},{"internalType":"uint256","name":"ticketPrice","type":"uint256"},{"internalType":"uint256","name":"closedTimestamp","type":"uint256"},{"internalType":"address","name":"nftAddress","type":"address"},{"internalType":"uint256","name":"tokenIdsCount","type":"uint256"},{"internalType":"address","name":"projectTreasury","type":"address"},{"internalType":"uint256","name":"raffleFeePercentage","type":"uint256"},{"internalType":"uint64","name":"sequenceNumber","type":"uint64"},{"internalType":"bytes32","name":"randomNumber","type":"bytes32"},{"internalType":"uint256","name":"enumerationIndex","type":"uint256"},{"internalType":"uint256","name":"totalTickets","type":"uint256"},{"internalType":"uint256","name":"prizesCount","type":"uint256"},{"internalType":"uint256","name":"distributedCount","type":"uint256"},{"internalType":"enum Minthub.RaffleStatus","name":"status","type":"uint8"}],"internalType":"struct Minthub.ConciseRaffleReport[]","name":"conciseRaffles_","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"}],"name":"getParticipationForUser","outputs":[{"internalType":"uint256","name":"listType_","type":"uint256"},{"internalType":"uint256[]","name":"raffleIds_","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPauseAndLockStatus","outputs":[{"internalType":"bool","name":"drawsPaused_","type":"bool"},{"internalType":"bool","name":"depositsPaused_","type":"bool"},{"internalType":"bool","name":"isLocked_","type":"bool"},{"internalType":"uint256","name":"unlockAtTimestamp_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"startIndex_","type":"uint256"},{"internalType":"uint256","name":"count_","type":"uint256"}],"name":"getPrizeDetailsForRaffle","outputs":[{"components":[{"internalType":"address","name":"winner","type":"address"},{"internalType":"uint32","name":"ticketNumber","type":"uint32"},{"internalType":"uint32","name":"prizeIndex","type":"uint32"},{"internalType":"enum Minthub.PrizeStatus","name":"prizeStatus","type":"uint8"}],"internalType":"struct Minthub.PrizeItem[]","name":"prizes_","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":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMembers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"startIndex_","type":"uint256"},{"internalType":"uint256","name":"count_","type":"uint256"}],"name":"getTicketRangesForRaffle","outputs":[{"internalType":"uint32[]","name":"ticketRanges_","type":"uint32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"startIndex_","type":"uint256"},{"internalType":"uint256","name":"count_","type":"uint256"}],"name":"getTokenIdsForRaffle","outputs":[{"internalType":"uint16[]","name":"tokenIds_","type":"uint16[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"uint256","name":"startIndex_","type":"uint256"},{"internalType":"uint256","name":"count_","type":"uint256"}],"name":"getUserPrizes","outputs":[{"components":[{"internalType":"uint256","name":"raffleId","type":"uint256"},{"internalType":"address","name":"nftAddress","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256","name":"raffleClosedTimestamp","type":"uint256"}],"internalType":"struct Minthub.UserNFTsReport[]","name":"userPrizes_","type":"tuple[]"}],"stateMutability":"view","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":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"address","name":"user_","type":"address"}],"name":"includeUserInRaffle","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingDefaultAdmin","outputs":[{"internalType":"address","name":"newAdmin","type":"address"},{"internalType":"uint48","name":"schedule","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingDefaultAdminDelay","outputs":[{"internalType":"uint48","name":"newDelay","type":"uint48"},{"internalType":"uint48","name":"schedule","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformFeeTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"raffleCount","outputs":[{"internalType":"uint256","name":"count_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"rescueERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress_","type":"address"},{"internalType":"uint256[]","name":"tokenIds_","type":"uint256[]"},{"internalType":"address","name":"to_","type":"address"}],"name":"rescueERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex_","type":"uint256"},{"internalType":"uint256","name":"count_","type":"uint256"},{"internalType":"uint256","name":"maxGasForNativeTokenTransfer_","type":"uint256"}],"name":"returnDepositsBelowMinimumBalance","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":"rollbackDefaultAdminDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"paused_","type":"bool"}],"name":"setDepositsPausedStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"paused_","type":"bool"}],"name":"setDrawsPausedStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"minimumBalance_","type":"uint256"}],"name":"setMinimumBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nftTreasury_","type":"address"}],"name":"setNftTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"platformFeePercentage_","type":"uint256"}],"name":"setPlatformFeePercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"platformFeeTreasury_","type":"address"}],"name":"setPlatformFeeTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"bool","name":"validatePrizeOwnership_","type":"bool"}],"name":"startDraw","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":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum Minthub.ParticipationListType","name":"listType_","type":"uint8"},{"internalType":"uint256[]","name":"raffleIds_","type":"uint256[]"}],"name":"updateParticipationAndOptionallyDeposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId_","type":"uint256"},{"internalType":"uint256","name":"ticketPrice_","type":"uint256"},{"internalType":"uint256","name":"closedTimestamp_","type":"uint256"},{"internalType":"address","name":"nftAddress_","type":"address"},{"internalType":"uint16[]","name":"tokenIds_","type":"uint16[]"},{"internalType":"address","name":"projectTreasury_","type":"address"},{"internalType":"uint256","name":"raffleFeePercentage_","type":"uint256"},{"internalType":"bool","name":"validatePrizeOwnership_","type":"bool"}],"name":"updateRaffle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"userCount","outputs":[{"internalType":"uint256","name":"count_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"value_","type":"uint256"},{"internalType":"uint256","name":"maxGasForNativeTokenTransfer_","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxGasForNativeTokenTransfer_","type":"uint256"}],"name":"withdrawCurrentBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxGasForNativeTokenTransfer_","type":"uint256"}],"name":"withdrawPlatformFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wrappedNativeToken","outputs":[{"internalType":"contract IWrappedNativeToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6101406040523461051b576170228038038061001a81610520565b9283398101906101808183031261051b5780519165ffffffffffff8316830361051b5761004960208301610545565b60408301519092906001600160401b03811161051b578261006b918301610559565b60608201519094906001600160401b03811161051b578361008d918401610559565b608083015190936001600160401b03821161051b576100ad918401610559565b9060a0830151926100c060c08201610545565b956100cd60e08301610545565b90610100830151906100e26101208501610545565b946100f66101606101408701519601610545565b916001600160a01b0381161561050557600180546001600160d01b031660d09390931b6001600160d01b03191692909217909155610133906105ed565b506001600455612a30608052607860a0526001600160a01b031660c05260005b89518110156101825760019061017b6001600160a01b03610174838e6105c3565b5116610678565b5001610153565b508760005b88518110156101b6576001906101af6001600160a01b036101a8838d6105c3565b51166106e4565b5001610187565b50600080516020616fc2833981519152600081815260208190527f6c9fbe396e57de500d525cbaec4c426195022646e1d232f522c0ab4b33e9089a8054600080516020617002833981519152918290558a9391929091907fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9080a460005b87518110156102635760019061025c6001600160a01b03610255838c6105c3565b5116610750565b5001610234565b50600080516020616fe2833981519152600081815260208190527f1cb33c18d374332f5007908f8e89d7c7568d510c67ab50b033541fb034170e1d805460008051602061700283398151915291829055899391929091907fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9080a46103e884116104c0576001600160a01b031693841561046c576001600160a01b0316918215610412576001600160a01b0316806103f95750600880546001600160a01b031916301790555b600655600980546001600160a01b0319169190911790556007556101005260e05260405161079c8082016001600160401b038111838210176103e3578291616826833903906000f080156103d75761012052604051615f7a90816108ac8239608051816154a7015260a05181615477015260c0518181816108e50152615396015260e0518181816110f101526145c80152610100518181816117da01526124bd01526101205181611c5d0152f35b6040513d6000823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b600880546001600160a01b031916919091179055610329565b60405162461bcd60e51b815260206004820152602c60248201527f506c6174666f726d206665652074726561737572792063616e6e6f742062652060448201526b7a65726f206164647265737360a01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602660248201527f456e74726f707920616464726573732063616e6e6f74206265207a65726f206160448201526564647265737360d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152601c60248201527f506c6174666f726d206665652065786365656473206d6178696d756d000000006044820152606490fd5b636116401160e11b600052600060045260246000fd5b600080fd5b6040519190601f01601f191682016001600160401b038111838210176103e357604052565b51906001600160a01b038216820361051b57565b9080601f8301121561051b578151916001600160401b0383116103e3578260051b90602080610589818501610520565b80968152019282010192831161051b57602001905b8282106105ab5750505090565b602080916105b884610545565b81520191019061059e565b80518210156105d75760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b600254906001600160a01b038216610667576001600160a01b0381166001600160a01b03199290921682176002556106269060006107bc565b9081610630575090565b600080526003602052610663907f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff610847565b5090565b631fe1e13d60e11b60005260046000fd5b610690816000805160206170028339815191526107bc565b908161069a575090565b6000805160206170028339815191526000526003602052610663906001600160a01b03167f2b196b93104bd6f7b096928bbf2e0612250eb7f333d06949e7a06ff41e8a7f41610847565b6106fc81600080516020616fc28339815191526107bc565b9081610706575090565b600080516020616fc28339815191526000526003602052610663906001600160a01b03167f435166c3b03f37cbf284086f7bbc24634185b9baf63e63e22a9f1af101f13f03610847565b61076881600080516020616fe28339815191526107bc565b9081610772575090565b600080516020616fe28339815191526000526003602052610663906001600160a01b03167f99c70e8277680714a040b536ed1559f48c9dceb8736d75f070b5b638f834a1d1610847565b6000818152602081815260408083206001600160a01b038616845290915290205460ff16610840576000818152602081815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b5050600090565b60018101908260005281602052604060002054156000146108a3578054680100000000000000008110156103e357600181018083558110156105d757839082600052602060002001555491600052602052604060002055600190565b50505060009056fe6080604052600436101561001e575b361561001957600080fd5b6149bd565b60003560e01c806301ffc9a714610439578063022d63fb1461043457806304350ee01461042f57806307973ccf1461042a5780630aa6220b1461042557806312545c7414610420578063150b7a021461041b57806317fcb39b146104165780631eb12a411461041157806323e3fbd51461040c578063248a9ca31461040757806326e2dca214610402578063285d8eaa146103fd578063287fb78a146103f85780632f2ff15d146103f3578063342f2df3146103ee5780633446e4b4146103e957806336568abe146103e45780633a8afede146103df5780633e3d5828146103da578063441a3e70146103d557806347b9385e146103d05780634aa181f0146103cb5780634ae215c2146103c65780634beb4922146103c157806352a5f1f8146103bc5780635fba3171146103b7578063634e93da146103b2578063649a5ec7146103ad578063678375a4146103a857806367f75527146103a35780636a734c231461039e5780636aa214161461039957806377662ffc146103945780637de3fd681461038f578063802895631461038a5780638333903e1461038557806383a992b11461038057806384ef8ffc1461037b5780638da5cb5b1461037b5780638df872f41461037657806391d14854146103715780639a0b79e51461036c578063a1eda53c14610367578063a217fddf14610362578063a3246ad31461035d578063a54c120c14610358578063a69df4b514610353578063a9268b491461034e578063aac5247714610349578063ab431aa714610344578063b9d1d49b1461033f578063cc8463c81461033a578063cdd78cfc14610335578063cefc142914610330578063cf6eefb71461032b578063d0e30db014610326578063d13a03ba14610321578063d17c62b61461031c578063d4a3e86214610317578063d4f3861914610312578063d547741f1461030d578063d602b9fd14610308578063ec2dc37c14610303578063f3136678146102fe5763f505b8b90361000e57613ac7565b613953565b613817565b6137b4565b61376a565b6136ec565b613604565b61350e565b613430565b61326d565b613233565b613182565b613164565b613139565b61311b565b613078565b613022565b612e37565b612df3565b612dd5565b612d5e565b612d42565b612cdf565b612cb6565b612c60565b612bf6565b612bb3565b612ab8565b612a14565b61285e565b612422565b61237f565b612295565b611f33565b611ef7565b611b4f565b611a55565b611974565b6118ad565b6117ad565b61170b565b6116c1565b611015565b610eb4565b610e95565b610e6d565b610d8a565b610c9b565b610c3b565b610c1d565b610bbd565b610b52565b610b0f565b6109f9565b610994565b61095a565b61091e565b6108cf565b610857565b610774565b6106ce565b6106b0565b6104f1565b6104b9565b346104a95760203660031901126104a95760043563ffffffff60e01b81168091036104a9576318a4c3c360e11b811490811561047e575b506040519015158152602090f35b637965db0b60e01b811491508115610498575b5038610470565b6301ffc9a760e01b14905038610491565b600080fd5b60009103126104a957565b346104a95760003660031901126104a9576020604051620697808152f35b60609060031901126104a957600435906024359060443590565b346104a9576104ff366104d7565b610507614a1b565b61051083613bc8565b50906213154c8201916213154d81019182549060009261052e614e53565b601354881015610672579083929188945061054d849888549085614ccf565b600282015490949283916001600160a01b031682915b87831061061a57505050916213154e6105c6927ffe24c1a52828fa9764a2d2ebf1c0066647f541fa9636c315facf936ed0b3a77996959450016105a7828254613c12565b9055604051938493846040919493926060820195825260208201520152565b0390a26105d36001600455565b80549283018093116106155782905554146105ea57005b426005557f2933cccdb74a2e7c335bb68961a839d56719c5f44c65a71d0ebae64da5805dbc600080a2005b613bee565b919a909293949596978261063a91856106338f8c613c12565b9189615519565b61065a575b61064a6001916143b2565b9a0190918b979695949392610563565b9461064a6106696001926143b2565b9691505061063f565b60405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9859999b19481251607a1b604482015280606481015b0390fd5b346104a95760003660031901126104a9576020600c54604051908152f35b346104a95760003660031901126104a9576106e7614a8d565b6002548060d01c80610706575b600280546001600160a01b031690555b005b42111561073857600180546001600160d01b031660309290921b6001600160d01b0319169190911790555b38806106f4565b507f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec5600080a1610731565b6001600160a01b038116036104a957565b346104a95760203660031901126104a95760043561079181610763565b610799614ae0565b6008546001600160a01b03821691908215610806576001600160a01b03198116909217600855604080516001600160a01b0393841681529290911660208301527f1e1cfbabc028aeb6656f7509a616634e3ca7cc87aa6865341b1dbddbe77e39719190819081015b0390a1005b60405162461bcd60e51b815260206004820152602360248201527f4e46542074726561737572792063616e6e6f74206265207a65726f206164647260448201526265737360e81b6064820152608490fd5b346104a95760803660031901126104a957610873600435610763565b61087e602435610763565b6064356001600160401b0381116104a957366023820112156104a95780600401356001600160401b0381116104a957369101602401116104a957604051630a85bd0160e11b815280602081015b0390f35b346104a95760003660031901126104a9576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b801515036104a957565b346104a95760a03660031901126104a95761070460043560643560443560243561094783610914565b6084359361095481613bc8565b50614b8d565b346104a95760203660031901126104a957602061098a60043561097c81610763565b6001600160a01b03166157ff565b6040519081529050f35b346104a95760203660031901126104a95760206109c1600435600052600060205260016040600020015490565b604051908152f35b9181601f840112156104a9578235916001600160401b0383116104a9576020808501948460051b0101116104a957565b346104a95760603660031901126104a957600435610a1681610763565b6024356001600160401b0381116104a957610a359036906004016109c9565b90604435610a4281610763565b610a4a614ae0565b6001600160a01b039093169260005b838110610a91576040517fbdf5d7ea06ca1bc0c9d914f24222184b986a7cf5cf6dfe8c5f9eb08a19f45bc99080610801878783613ce9565b610a9c818585613c36565b3590853b156104a957604051632142170760e11b81523060048201526001600160a01b03841660248201526044810192909252600082606481838a5af1918215610b0a57600192610aef575b5001610a59565b80610afe6000610b0493613c98565b806104ae565b38610ae8565b613cb9565b346104a95760003660031901126104a957608060095460055460ff60405192818160a01c161515845260a81c161515602083015280421060408301526060820152f35b346104a957610b60366104d7565b9060135483101561067257610b7492613d63565b60405180916020820160208352815180915260206040840192019060005b818110610ba0575050500390f35b825161ffff16845285945060209384019390920191600101610b92565b346104a95760403660031901126104a957600435602435610bdd81610763565b8115610c0c5781610c07610c0261070494600052600060205260016040600020015490565b614b52565b6158aa565b631fe1e13d60e11b60005260046000fd5b346104a95760003660031901126104a9576020600a54604051908152f35b346104a95760203660031901126104a9577f9e6ff0034d37f9ece2fbd9c659a830b4544125e54557e07e79631d9284e60c8a600435610c78614ae0565b600654816006556108016040519283928360209093929193604081019481520152565b346104a95760403660031901126104a957600435602435610cbb81610763565b811580610d5c575b610cd1575b61070491614d60565b60015465ffffffffffff60a082901c1692906001600160a01b031615801590610d48575b8015610d36575b610d19576001805465ffffffffffff60a01b191690559150610cc8565b6319ca5ebb60e01b60005265ffffffffffff831660045260246000fd5b504265ffffffffffff84161015610cfc565b5065ffffffffffff831615610cf5565b1590565b50600254610d7a906001600160a01b03165b6001600160a01b031690565b6001600160a01b03821614610cc3565b60403660031901126104a9576004356001600160401b0381116104a957610db59036906004016109c9565b60243590610dc1614ae0565b610dcb8183613e04565b3403610e025760005b818110610ddd57005b80610dfc84610df7610df2600195878a613c36565b613e17565b614d8e565b01610dd4565b60405162461bcd60e51b815260206004820152603d60248201527f546f74616c206465706f73697420646f6573206e6f7420657175616c2074686560448201527f2073756d206f6620746865206465706f736974732070657220757365720000006064820152608490fd5b346104a95760203660031901126104a9576020610e8b600435613e36565b6040519015158152f35b346104a95760403660031901126104a957610704602435600435613ee8565b60403660031901126104a95760043560048110156104a9576024356001600160401b0381116104a957610eeb9036906004016109c9565b6005929192544210610fe757336000908152601160205260409020610f11908390614026565b336000908152601160205260409020610f2c90600101615a22565b610f358261401c565b81151580610fd3575b610f91575b7fefb613bc14d1a231a39f4fe36a840921cc7b01e48c25241fc92d775bcb3af966919234610f82575b610f7d60405192839233968461403e565b0390a2005b610f8c3433614d8e565b610f6c565b60005b818110610fa15750610f43565b33600090815260116020526040902060019190610fcc908301610fc5838689613c36565b3590615a60565b5001610f94565b50610fdd8261401c565b6003821415610f3e565b60405162461bcd60e51b8152602060048201526006602482015265131bd8dad95960d21b6044820152606490fd5b346104a9576101003660031901126104a95760043560643560443560243561103c83610763565b6084356001600160401b0381116104a95761105b9036906004016109c9565b60a4949194359061106b82610763565b60c4359260e4359661107c88610914565b611084614ae0565b6013548910156106725760ff6213154f61109d8b613bc8565b5001541660068110156116bc57611677578515611632576001600160a01b0382169788156115ed5783156115b5576001600160a01b0385161561157057612710861161152b5761271084116114e6576111167f000000000000000000000000000000000000000000000000000000000000000042613c12565b8810611489576008546001600160a01b03166001600160a01b0381163014159081611414575b506113b4579790899796959493929161115489613bc8565b50878155600181018990556002810180546001600160a01b0319166001600160a01b0386161790559960038b019060005b8681106112515750506102748b018590556102758b0180546001600160a01b0319166001600160a01b038881169182179092556102768d018990556040805160008152602081018c90529081018c905291861660608301526080820187905260a082015260c081018890528a90611232906213154f908e9084907fb29e735fcfbbcb24f5742786b6f34964d70f3b0ee0b32d45f7411961f6e0c640908060e081015b0390a2015460ff1690565b9061123c82613841565b600080516020615f05833981519152600080a3005b908080939495969798999a9b9c5090611312575b6112b857806112a661128261127d6001948b8a613c36565b614e44565b61128c8387613d32565b90919061ffff8084549260031b9316831b921b1916179055565b01908c9a999897969594939291611185565b60405162461bcd60e51b815260206004820152602c60248201527f4e465420547265617375727920646f6573206e6f7420686f6c6420616c6c204560448201526b524337323120746f6b656e7360a01b6064820152608490fd5b5061134a602061132661127d848b8a613c36565b6040516331a9108f60e11b815261ffff909116600482015291829081906024820190565b0381885afa908115610b0a57600091611386575b50600854611374906001600160a01b0316610d6e565b6001600160a01b039091161415611265565b6113a7915060203d81116113ad575b61139f8183613c98565b810190613e21565b3861135e565b503d611395565b60405162461bcd60e51b815260206004820152603260248201527f4e4654205472656173757279206d75737420617070726f7665204d696e7468756044820152716220746f207472616e73666572204e46547360701b6064820152608490fd5b60405163e985e9c560e01b81526001600160a01b0391909116600482015230602482015290506020816044818d5afa908115610b0a5760009161145a575b50153861113c565b61147c915060203d602011611482575b6114748183613c98565b810190614253565b38611452565b503d61146a565b60405162461bcd60e51b815260206004820152602f60248201527f526166666c65206d757374206265206f70656e20666f72206174206c6561737460448201526e081b5a5b9a5b5d5b481c195c9a5bd9608a1b6064820152608490fd5b60405162461bcd60e51b815260206004820152601c60248201527f546f6b656e204944732065786365656473206d6178207072697a6573000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601860248201527f4665652069732067726561746572207468616e203130302500000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601f60248201527f50726f6a6563742074726561737572792063616e6e6f74206265207a65726f006044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665207072697a657360801b6044820152606490fd5b60405162461bcd60e51b815260206004820152601a60248201527f4e465420616464726573732063616e6e6f74206265207a65726f0000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601860248201527f5469636b6574207072696365206d757374206265203e203000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820181905260248201527f526166666c65206973206e6f7420696e207072652064726177207374617475736044820152606490fd5b61304b565b346104a95760203660031901126104a9576107046004356116e1336157ff565b9050613ee8565b60609060031901126104a95760043561170081610763565b906024359060443590565b346104a957611719366116e8565b90611722614e53565b6013548110156106725761173592614e75565b15611744576107046001600455565b60405162461bcd60e51b815260206004820152602a60248201527f4e6f207072697a657320666f72207573657220696e20726166666c6520746f206044820152696469737472696275746560b01b6064820152608490fd5b6001600160401b038116036104a957565b346104a95760603660031901126104a9576004356117ca8161179c565b6117d5602435610763565b6044357f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031680156118685733036118175761070491614f49565b60405162461bcd60e51b815260206004820152602360248201527f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e637460448201526234b7b760e91b6064820152608490fd5b60405162461bcd60e51b815260206004820152601760248201527f456e74726f70792061646472657373206e6f74207365740000000000000000006044820152606490fd5b346104a95760203660031901126104a9576004356118c9614ae0565b6013548110156106725760ff6213154f6118e283613bc8565b5001541660068110156116bc5760041461192f57806119106001611907600594613bc8565b50600019910155565b61123c6213154f61192083613bc8565b5001805460ff19166005179055565b60405162461bcd60e51b815260206004820181905260248201527f43616e6e6f742063616e63656c206120636f6d706c6574656420726166666c656044820152606490fd5b346104a95760203660031901126104a95760043561199181610763565b611999614a8d565b7f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed660206119d66119c842615ab7565b6119d06144e7565b90615047565b600180546001600160d01b031981166001600160a01b039690961695861760a084811b65ffffffffffff60a01b169190911790925565ffffffffffff911c16611a2b575b65ffffffffffff60405191168152a2005b7f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a9605109600080a1611a1a565b346104a95760203660031901126104a95760043565ffffffffffff8116908181036104a957611a82614a8d565b611a8b42615ab7565b9165ffffffffffff611a9b6144e7565b1680821115611b1557507ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b9265ffffffffffff826206978080611ae8951091180262069780181690615047565b90611af3828261573d565b6040805165ffffffffffff928316815292909116602083015281908101610801565b0365ffffffffffff8111610615577ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b92611ae89190615047565b346104a95760403660031901126104a957600435602435906013548110156106725762131550611b7e82613bc8565b5001805480611e9a57504290555b611b94614a1b565b8115611e6457611ba381613bc8565b506213154f8101906003611bb8835460ff1690565b611bc181613841565b03611e10576213154a810154918215611dde57611c1d9482610274610278940154808610600014611dd55750600085925b6213154c830192611c598454978893611c0b85896140b8565b8082108714611dcb57509b8c9a6140a9565b9201546040519b8c94859463ac7dc46760e01b86526004860190949360069360809360a084019784526020840152604083015260608201520152565b03817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa968715610b0a57600097611da8575b5060005b858110611d065750917f1fd2691832f2c75387438682910304c91d73ece89b0e6123fe3813ddad1ed70d959691610f7d935414611cdf575b506040519384938461414c565b805460ff19166004179055600486600080516020615f05833981519152600080a338611cd2565b600190611d826000828b63ffffffff611d38611d2f611d258585613d4f565b5163ffffffff1690565b63ffffffff1690565b03611d885750611d489089613c12565b8a7f66a8c5b89438eb03696fbf4fae05d99dcfa81867a18c352b258b5de0aea1c246600080a35b611d7c611d25848d613d4f565b8a615165565b01611c9a565b611da39250611d25611d9d92611d2f92613d4f565b8a615065565b611d6f565b611dc491973d8091833e611dbc8183613c98565b8101906140c5565b9538611c96565b90509b8c9a6140a9565b60009092611bf2565b60405162461bcd60e51b815260206004820152600a6024820152694e6f207469636b65747360b01b6044820152606490fd5b60405162461bcd60e51b815260206004820152602660248201527f526166666c65206e6f7420726561647920666f722077696e6e65722067656e656044820152653930ba34b7b760d11b6064820152608490fd5b60405162461bcd60e51b815260206004820152600e60248201526d0426174636853697a6520697320360941b6044820152606490fd5b9050600b5410611b8c5760405162461bcd60e51b8152806106ac6004820161405f565b906020808351928381520192019060005b818110611edb5750505090565b825163ffffffff16845260209384019390920191600101611ece565b346104a957611f05366104d7565b601392919254821015610672576108cb92611f1f92614186565b604051918291602083526020830190611ebd565b346104a95760403660031901126104a9576004356024356013548210156106725762131550611f6183613bc8565b500180548061227257504290555b611f77614a1b565b611f8082613bc8565b50906213154f8201916002611f96845460ff1690565b611f9f81613841565b0361221957611fbc611fb447600a54906140b8565b825490614233565b63fffffffe1061218f57600c54926102798201621315498301611fe181549586613c12565b958087101561215b575b508590556213154a83019081549354855b87811061206957887fd5be8ab785440155ef7ce08e886cd5ab029663ba21e3c3aa702f68a30fd7799f60038a610f7d8b8b61203b8c828d555460ff1690565b9361204585613841565b60408051938452602084019190915282015292909114606083015281906080820190565b886120738261531f565b91908892612081828461486e565b61210f575b50907fc82b78cab3e074fc5b75f2f9e68a9d8aadd66032fac0293513c12d5925c470268493926120df8b63ffffffff6120c16001998c614168565b919092169063ffffffff8084549260031b9316831b921b1916179055565b6120e9848c6140b8565b60408051878152602081019690965285015260a086901b8690031692606090a301611ffc565b6121506001959493929a61214a611d2f611d2f8a7fc82b78cab3e074fc5b75f2f9e68a9d8aadd66032fac0293513c12d5925c4702696614233565b90613c12565b999192939450612086565b6213154b8501819055835460ff1916600317845595508590600388600080516020615f05833981519152600080a390611feb565b60405162461bcd60e51b815260206004820152605660248201527f546f74616c207469636b6574732065786365656473206d6178696d756d20706f60448201527f737369626c65207469636b6574733a2063616e6e6f7420656e756d65726174656064820152751030ba103a3434b9903a34b1b5b2ba10383934b1b29760511b608482015260a490fd5b60405162461bcd60e51b815260206004820152602b60248201527f526166666c65206973206e6f7420696e2072616e646f6d6e657373207265636560448201526a697665642073746174757360a81b6064820152608490fd5b9050600b5410611f6f5760405162461bcd60e51b8152806106ac6004820161405f565b346104a95760203660031901126104a9576004356122b1614e53565b6009546001600160a01b03161561233a57600a549081156122f5576122eb916122da6000600a55565b6009546001600160a01b0316615369565b6107046001600455565b60405162461bcd60e51b815260206004820152601c60248201527f4e6f20706c6174666f726d206665657320746f207769746864726177000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601d60248201527f506c6174666f726d20666565207472656173757279206e6f74207365740000006044820152606490fd5b346104a95760603660031901126104a9576123f160206004356123a181610763565b602435906044356123b181610763565b6123b9614ae0565b60405163a9059cbb60e01b81526001600160a01b03909116600482015260248101929092529092839190829060009082906044820190565b03926001600160a01b03165af18015610b0a5761240a57005b6107049060203d602011611482576114748183613c98565b60403660031901126104a95760243560043561243d82610914565b6013548110156106725760ff60095460a01c166127645761245c614a1b565b61246581613bc8565b50916001830154421061271f576213154f830190612484825460ff1690565b61248d81613841565b611677578061270d575b6112b857805460ff191660011790556124ae615472565b6040516341025b3d60e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690602081600481855afa8015610b0a576001600160801b03916000916126de575b50169081341061268d5781341161261d575b602090600460405180948193637b43155d60e01b83525af1908115610b0a576000916125ee575b506001600160401b038116156125a9576125938161257a849361027760019701906001600160401b03166001600160401b0319825416179055565b6001600160401b03166000526012602052604060002090565b55600080516020615f05833981519152600080a3005b60405162461bcd60e51b815260206004820152601760248201527f496e76616c69642073657175656e6365206e756d6265720000000000000000006044820152606490fd5b612610915060203d602011612616575b6126088183613c98565b8101906142e7565b3861253f565b503d6125fe565b600080808061262c86346140b8565b335af16126376142b7565b506125185760405162461bcd60e51b8152602060048201526024808201527f4661696c656420746f20726566756e642065786365737320656e74726f70792060448201526318dbdcdd60e21b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f496e73756666696369656e742076616c756520666f7220656e74726f707920636044820152621bdcdd60ea1b6064820152608490fd5b612700915060203d602011612706575b6126f88183613c98565b810190614268565b38612506565b503d6126ee565b5061271a610d5883613e36565b612497565b60405162461bcd60e51b815260206004820152601860248201527f526166666c65206973206e6f742079657420636c6f73656400000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f111c985ddcc8185c99481c185d5cd95960821b6044820152606490fd5b906020808351928381520192019060005b8181106127ba5750505090565b82518452602093840193909201916001016127ad565b602081016020825282518091526040820191602060408360051b8301019401926000915b83831061280357505050505090565b9091929394602080600192603f1985820301865288519081518152848060a01b03838301511683820152606080612849604085015160806040860152608085019061279c565b930151910152970193019301919392906127f4565b346104a95761289261286f366116e8565b6001600160a01b0383166000908152601060205260409020919391549084614ccf565b61289b816142fc565b916000935b8285106128b557604051806108cb86826127d0565b6001600160a01b03821660009081526010602052604090206128ef906128e5906128df8885613c12565b90614368565b90549060031b1c90565b946128f986613bc8565b509561292a8161291b8660018060a01b0316600052600f602052604060002090565b90600052602052604060002090565b549661293588614380565b600382019860005b8181106129ae575050600194959697985061298485612965600285015460018060a01b031690565b93015492612971613cfa565b9485526001600160a01b03166020850152565b604083015260608201526129988288613d4f565b526129a38187613d4f565b5001939291906128a0565b80612a036129fc6129ee8e6129e86128e56001978f8d61291b6129e39260018060a01b0316600052600f602052604060002090565b614368565b90613d32565b905461ffff9160031b1c1690565b61ffff1690565b612a0d8286613d4f565b520161293d565b346104a95760203660031901126104a957600435612a30614ae0565b600754906103e88111612a7d5760078190556040805192835260208301919091527f91cf3aa4a4860dcd0e8a1db910306400c8432212e7f52c5e3dfd3d0b79f8277a919081908101610801565b60405162461bcd60e51b81526020600482015260136024820152724665652065786365656473206d6178696d756d60681b6044820152606490fd5b346104a957612ac6366104d7565b612ad1929192614e53565b60135482101561067257612ae482613bc8565b50926213154c840154811015612b785760018060a01b03600285015416600554421080612b3f575b610fe757612b1a938561556e565b612b28576107046001600455565b6213154e01612b3781546143b2565b9055386122eb565b503360009081527f1cb33c18d374332f5007908f8e89d7c7568d510c67ab50b033541fb034170e1c602052604090205460ff1615612b0c565b60405162461bcd60e51b8152602060048201526013602482015272092dcecc2d8d2c840e0e4d2f4ca40d2dcc8caf606b1b6044820152606490fd5b346104a95760003660031901126104a9576002546040516001600160a01b039091168152602090f35b604090612bf393928152816020820152019061279c565b90565b346104a95760203660031901126104a957600435612c1381610763565b6001600160a01b031660008181526011602052604090205460ff169060048210156116bc576000526011602052612c506001604060002001615cad565b906108cb60405192839283612bdc565b346104a95760403660031901126104a957602060ff612caa602435600435612c8782610763565b6000526000845260406000209060018060a01b0316600052602052604060002090565b54166040519015158152f35b346104a95760003660031901126104a9576009546040516001600160a01b039091168152602090f35b346104a95760003660031901126104a9576002548060d01c9081151580612d38575b15612d2e5760a01c65ffffffffffff165b6040805165ffffffffffff928316815292909116602083015290f35b5050600080612d12565b5042821015612d01565b346104a95760003660031901126104a957602060405160008152f35b346104a95760203660031901126104a9576004356000526003602052612d876040600020615cad565b60405180916020820160208352815180915260206040840192019060005b818110612db3575050500390f35b82516001600160a01b0316845285945060209384019390920191600101612da5565b346104a95760003660031901126104a9576020601354604051908152f35b346104a95760003660031901126104a957612e0c614ae0565b426005557f7e80c098f669ba3a098abdc0e1112d3229b26c098a48dc2701b7802336f5845c600080a1005b346104a957612e5d612e48366104d7565b919290612e53614ae0565b600c549084614ccf565b805b612e6557005b80830180841161061557600019810190811161061557612e849061531f565b9060065482101561301b57612e97614e53565b6005544210610fe757612ea981613c1f565b808311612fdd5782612eba916140b8565b80612f7757600080516020615f2583398151915291612ee9612ee46001600160a01b038316610d6e565b615cf8565b505b612f02612efa60075486613e04565b612710900490565b612f20612f1b612f1283886140b8565b92600a54613c12565b600a55565b8681612f66575b5050612f3242600b55565b6040805194855260208501929092526001600160a01b031692a2612f566001600455565b8015610615576000190180612e5f565b612f709183615369565b3886612f27565b6006548110612fa05781612f9a82600080516020615f25833981519152946159fc565b50612eeb565b60405162461bcd60e51b815260206004820152601560248201527442616c616e63652062656c6f77206d696e696d756d60581b6044820152606490fd5b60405162461bcd60e51b8152602060048201526016602482015275416d6f756e7420657863656564732062616c616e636560501b6044820152606490fd5b5050612f56565b346104a95760003660031901126104a9576008546040516001600160a01b039091168152602090f35b634e487b7160e01b600052602160045260246000fd5b600311156116bc57565b9060038210156116bc5752565b346104a957613086366104d7565b906013548310156106725761309a9261440f565b60405180916020820160208352815180915260206040840192019060005b8181106130c6575050500390f35b919350916020608060019261310d60608851868060a01b03815116845263ffffffff86820151168685015263ffffffff60408201511660408501520151606083019061306b565b0194019101918493926130b8565b346104a95760003660031901126104a9576020600654604051908152f35b346104a95760003660031901126104a95760206131546144e7565b65ffffffffffff60405191168152f35b346104a95760003660031901126104a9576020600754604051908152f35b346104a95760003660031901126104a9576001546001600160a01b0316330361321e5760015460a081901c65ffffffffffff16906001600160a01b031681158015613214575b6131ff576002546131ec91906131e6906001600160a01b0316615916565b50615834565b50600180546001600160d01b0319169055005b506319ca5ebb60e01b60005260045260246000fd5b50428210156131c8565b636116401160e11b6000523360045260246000fd5b346104a95760003660031901126104a957600154604080516001600160a01b038316815260a09290921c65ffffffffffff16602083015290f35b60003660031901126104a9576005544210610fe75760ff60095460a81c166133f55733156133b0573415613375576132ad346132a833613c1f565b613c12565b6006548110612fa0576132c081336159fc565b908180613367575b613322576132d542600b55565b7f3d7ca2b1d4d92b9f80e6594957453c9542774babe911cd93d523d44fcfd019b66040518061331d339533953484919260409194936060840195845260208401521515910152565b0390a3005b60405162461bcd60e51b815260206004820152601a60248201527f4d617820706c6174666f726d20757365727320726561636865640000000000006044820152606490fd5b5062989680600c54116132c8565b60405162461bcd60e51b815260206004820152601360248201527243616e6e6f74206465706f736974207a65726f60681b6044820152606490fd5b60405162461bcd60e51b815260206004820152601b60248201527f557365722063616e6e6f74206265207a65726f206164647265737300000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601360248201527211195c1bdcda5d1cc8185c99481c185d5cd959606a1b6044820152606490fd5b346104a95760403660031901126104a95760043561344d81610763565b602435613458614e53565b6001600160a01b0382166000818152601060205260408120549093849391925b8385106134d457851561348f576107046001600455565b60405162461bcd60e51b815260206004820181905260248201527f4e6f207072697a657320666f72207573657220746f20646973747269627574656044820152606490fd5b909192939461350360019183600052601060205261214a866134fd6128e58b6040600020614368565b87614e75565b950193929190613478565b346104a95760203660031901126104a95760043561352b81610914565b613533614ae0565b60095460a01c60ff16151581151514613599576009805460ff60a01b191682151560a01b60ff60a01b161790557fd42e3119c2424e39164970a3305ba51bd6f674514318dfb7e5c1c2bc3fb0f03e90610801905b60405190151581529081906020820190565b60405162461bcd60e51b815260206004820152603760248201527f4472617773207061757365642073746174757320697320616c7265616479207360448201527f657420746f2074686520646573697265642076616c75650000000000000000006064820152608490fd5b346104a95760203660031901126104a95760043561362181610914565b613629614ae0565b60095460a81c60ff16151581151514613681576009805460ff60a81b191682151560a81b60ff60a81b161790557f9cc2044889457670dcf1b4f002e64d84b8507017b30f77aab31d88416a23c4cd9061080190613587565b60405162461bcd60e51b815260206004820152603a60248201527f4465706f73697473207061757365642073746174757320697320616c7265616460448201527f792073657420746f2074686520646573697265642076616c75650000000000006064820152608490fd5b346104a95760e03660031901126104a95760443560243560043561370f83610763565b606435916001600160401b0383116104a9576108cb9361373661375a9436906004016109c9565b906084359261374484610763565b60a4359460c4359661375588610914565b614520565b6040519081529081906020820190565b346104a95760403660031901126104a95760043560243561378a81610763565b8115610c0c57816137af610c0261070494600052600060205260016040600020015490565b61598c565b346104a95760003660031901126104a9576137cd614a8d565b600180546001600160d01b0319811690915560a01c65ffffffffffff166137f057005b7f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a9605109600080a1005b346104a95760403660031901126104a9576020610e8b60243560043561383c82610763565b61486e565b600611156116bc57565b9060068210156116bc5752565b602060408183019282815284518094520192019060005b81811061387c5750505090565b90919260206101c06001926139486101a08851805184528581015186850152604081015160408501526138be6060820151606086019060018060a01b03169052565b6080818101519085015260a0808201516001600160a01b03169085015260c081015160c08501526138ff60e082015160e08601906001600160401b03169052565b61010081015161010085015261012081015161012085015261014081015161014085015261016081015161016085015261018081015161018085015201516101a083019061384b565b01940192910161386f565b346104a95760403660031901126104a9576004356139776024356013549083614ccf565b90613981826148f9565b9060005b83811061399a57604051806108cb8582613858565b6139a48183613c12565b6139ad90613bc8565b506139b88284613c12565b815460018301546002840154929391926001600160a01b03166102748301546102758401546001600160a01b03166102768501546102778601546001600160401b0316906102788701549262131549880154946213154a890154966213154c8a0154986213154e8b01549a6213154f0154613a339060ff1690565b9b613a3c613d0b565b9d8e5260208e015260408d01526001600160a01b031660608c015260808b01526001600160a01b031660a08a015260c08901526001600160401b031660e0880152610100870152610120860152610140850152610160840152610180830152613aa9906101a083016149b1565b613ab38285613d4f565b52613abe8184613d4f565b50600101613985565b346104a95760203660031901126104a957600435613ae481610763565b613aec614ae0565b6009546001600160a01b03821691908215613b58576001600160a01b03198116909217600955604080516001600160a01b0393841681529290911660208301527f812b3ab9a36a8650971353f9b65095ff5cd4b2104e27b9c9eff6b32757516864919081908101610801565b60405162461bcd60e51b815260206004820152602c60248201527f506c6174666f726d206665652074726561737572792063616e6e6f742062652060448201526b7a65726f206164647265737360a01b6064820152608490fd5b634e487b7160e01b600052603260045260246000fd5b601354811015613be957601360005262131551602060002091020190600090565b613bb2565b634e487b7160e01b600052601160045260246000fd5b906001820180921161061557565b9190820180921161061557565b613c31906001600160a01b03166157ff565b905090565b9190811015613be95760051b0190565b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b03821117613c7757604052565b613c46565b6101c081019081106001600160401b03821117613c7757604052565b90601f801991011681019081106001600160401b03821117613c7757604052565b6040513d6000823e3d90fd5b81835290916001600160fb1b0383116104a95760209260051b809284830137010190565b916020612bf3938181520191613cc5565b60405190613d09608083613c98565b565b60405190613d096101c083613c98565b6001600160401b038111613c775760051b60200190565b919091612710831015613be957601e908360041c019260011b1690565b8051821015613be95760209160051b010190565b613d6c90613bc8565b50916102748301548015613dfb57613d849183614ccf565b613d8d81613d1b565b92613d9b6040519485613c98565b818452601f19613daa83613d1b565b0136602086013760030160005b828110613dc5575050505090565b8084019081851161061557613df5613de26129ee60019486613d32565b613dec8389613d4f565b9061ffff169052565b01613db7565b50505050606090565b8181029291811591840414171561061557565b35612bf381610763565b908160209103126104a95751612bf381610763565b613e3f90613bc8565b50600281015461027482015460085490926000926001600160a01b0390811692600390920191613e6f9116610d6e565b925b848110613e82575050505050600190565b613e9460206113266129ee8486613d32565b0381875afa8015610b0a578591600091613eca575b506001600160a01b031603613ec057600101613e71565b5050505050600090565b613ee2915060203d81116113ad5761139f8183613c98565b38613ea9565b908115613fe057613ef7614e53565b6005544210610fe757613f0933613c1f565b808311612fdd5782613f1a916140b8565b9081613fb357600080516020615f2583398151915291613f9591613f40612ee433610d6e565b505b613f51612efa60075487613e04565b90613f6b612f1b613f6284896140b8565b93600a54613c12565b81613fa2575b5050613f7c42600b55565b6040805194855260208501919091523393918291820190565b0390a2613d096001600455565b613fac9133615369565b3880613f71565b6006548210612fa05781613f9591613fda600080516020615f2583398151915294336159fc565b50613f42565b60405162461bcd60e51b815260206004820152601460248201527343616e6e6f74207769746864726177207a65726f60601b6044820152606490fd5b600411156116bc57565b9060048110156116bc5760ff80198354169116179055565b929060048110156116bc5783604091612bf395528160208201520191613cc5565b60809060208152602960208201527f42616c616e6365732068617665206368616e67656420647572696e6720647261604082015268772070726f6365737360b81b60608201520190565b60001981019190821161061557565b9190820391821161061557565b6020818303126104a9578051906001600160401b0382116104a957019080601f830112156104a9578151906140f982613d1b565b926141076040519485613c98565b82845260208085019360051b8201019182116104a957602001915b81831061412f5750505090565b825163ffffffff811681036104a957815260209283019201614122565b612bf39392606092825260208201528160408201520190611ebd565b91909162989680831015613be957601c908360031c019260021b1690565b61418f90613bc8565b50916213154b61027984019301548015613dfb576141ad9183614ccf565b6141b681613d1b565b926141c46040519485613c98565b818452601f196141d383613d1b565b0136602086013760005b8281106141eb575050505090565b808401908185116106155761422d61421861420860019486614168565b905463ffffffff9160031b1c1690565b6142228389613d4f565b9063ffffffff169052565b016141dd565b811561423d570490565b634e487b7160e01b600052601260045260246000fd5b908160209103126104a95751612bf381610914565b908160209103126104a957516001600160801b03811681036104a95790565b6001600160401b038111613c7757601f01601f191660200190565b604051906142b1602083613c98565b60008252565b3d156142e2573d906142c882614287565b916142d66040519384613c98565b82523d6000602084013e565b606090565b908160209103126104a95751612bf38161179c565b9061430682613d1b565b6143136040519182613c98565b8281528092614324601f1991613d1b565b019060005b82811061433557505050565b60209060405161434481613c5c565b60008152600083820152606060408201526000606082015282828501015201614329565b8054821015613be95760005260206000200190600090565b9061438a82613d1b565b6143976040519182613c98565b82815280926143a8601f1991613d1b565b0190602036910137565b60001981146106155760010190565b906040516143ce81613c5c565b91546001600160a01b038116835260a081901c63ffffffff908116602085015260c082901c16604084015260e01c60ff168260038210156116bc5760600152565b61441f6213154c91939293613bc8565b50019081548015613dfb576144349184614ccf565b9061443e82613d1b565b9261444c6040519485613c98565b828452601f1961445b84613d1b565b0160005b8181106144b457505060005b838110614479575050505090565b8061449861449261448c60019486613c12565b86614368565b506143c1565b6144a28288613d4f565b526144ad8187613d4f565b500161446b565b6020906040516144c381613c5c565b6000815260008382015260006040820152600060608201528282890101520161445f565b6002548060d01c8015159081614516575b501561450c5760a01c65ffffffffffff1690565b5060015460d01c90565b90504211386144f8565b3360009081527f6c9fbe396e57de500d525cbaec4c426195022646e1d232f522c0ab4b33e90899602052604090205493979691959094919390929060ff16156148355760135497600160401b891015613c77576001890160135561458389613bc8565b50508515611632576001600160a01b0382169788156115ed5783156115b5576001600160a01b0385161561157057612710861161152b5761271084116114e6576145ed7f000000000000000000000000000000000000000000000000000000000000000042613c12565b8810611489576008546001600160a01b03166001600160a01b03811630141590816147d0575b506113b4579790899796959493929161462b89613bc8565b50878155600181018990556002810180546001600160a01b0319166001600160a01b0386161790559960038b019060005b8681106147245750505050506102748801829055610275880180546001600160a01b0319166001600160a01b039485169081179091556102768901859055604080516001815260208101979097528601969096529091166060840152608083015260a082019290925260c0810191909152614704926213154f9290917fb29e735fcfbbcb24f5742786b6f34964d70f3b0ee0b32d45f7411961f6e0c640908060e08101611227565b61470d81613841565b81600080516020615f05833981519152600080a390565b908080939495969798999a9b9c5090614762575b6112b8578061475061128261127d6001948b8a613c36565b01908c9a99989796959493929161465c565b50614776602061132661127d848b8a613c36565b0381885afa908115610b0a576000916147b2575b506008546147a0906001600160a01b0316610d6e565b6001600160a01b039091161415614738565b6147ca915060203d81116113ad5761139f8183613c98565b3861478a565b60405163e985e9c560e01b81526001600160a01b0391909116600482015230602482015290506020816044818d5afa908115610b0a57600091614816575b501538614613565b61482f915060203d602011611482576114748183613c98565b3861480e565b63e2517d3f60e01b600052336004527f5b4b8088d200445bfb44acef5ae68c0906d30def45a3f841c2e4c6f0a2b1e91460245260446000fd5b9060018060a01b0316600052601160205260406000209060ff8254166148938161401c565b80156148f1576148a28161401c565b600381146148e957806148b660019261401c565b146148d257600090815260029190910160205260409020541590565b600090815260029091016020526040902054151590565b505050600090565b505050600190565b9061490382613d1b565b6149106040519182613c98565b8281528092614921601f1991613d1b565b019060005b82811061493257505050565b60209060405161494181613c7c565b60008152600083820152600060408201526000606082015260006080820152600060a0820152600060c0820152600060e08201526000610100820152600061012082015260006101408201526000610160820152600061018082015260006101a082015282828501015201614926565b60068210156116bc5752565b60405162461bcd60e51b815260206004820152603060248201527f43616e6e6f74207265636569766520455448206469726563746c792c2075736560448201526f2070617961626c65206d6574686f647360801b6064820152608490fd5b3360009081527f1cb33c18d374332f5007908f8e89d7c7568d510c67ab50b033541fb034170e1c602052604090205460ff1615614a5457565b63e2517d3f60e01b600052336004527f52e466598c5e4408cbbaf840ea4dbd39f3e0aa1ba17a69b571858fba5698291f60245260446000fd5b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff1615614ac657565b63e2517d3f60e01b60005233600452600060245260446000fd5b3360009081527f0790b0aaeb4157b4822815d95bf3d9318a1561c876c520e6cca704be6f4d63a6602052604090205460ff1615614b1957565b63e2517d3f60e01b600052336004527f5246556c0ab216b5b325ad7c539bfbd1a07c768773cdf810fecd3c3375c2740760245260446000fd5b60008181526020818152604080832033845290915290205460ff1615614b755750565b63e2517d3f60e01b6000523360045260245260446000fd5b949290939194614b9b614e53565b60135485101561067257614bb96000966213154c8301549084614ccf565b6002820154600095919491906001600160a01b03166000915b868310614c855750505080614c7d575b614c2a577ffe24c1a52828fa9764a2d2ebf1c0066647f541fa9636c315facf936ed0b3a779936213154e614c1c92016105a7828254613c12565b0390a290613d096001600455565b60405162461bcd60e51b815260206004820152602560248201527f4e6f207072697a657320746f206469737472696275746520696e2074686973206044820152640c4c2e8c6d60db1b6064820152608490fd5b508315614be2565b909198614c9e82614c968c89613c12565b858c89615519565b614cb7575b614cae6001916143b2565b99019190614bd2565b96614cae614cc66001926143b2565b98915050614ca3565b9082821015614d0b5780158015614cf9575b15614cf3575081039081116106155790565b91505090565b50808201808311610615578310614ce1565b60405162461bcd60e51b815260206004820152602760248201527f537461727420696e646578206d757374206265206c657373207468616e20746860448201526619481d1bdd185b60ca1b6064820152608490fd5b90336001600160a01b03821603614d7d57614d7a9161598c565b50565b63334bd91960e11b60005260046000fd5b906005544210610fe75760ff60095460a81c166133f5576001600160a01b0382169182156133b057811561337557614dc9826132a883613c1f565b906006548210612fa05781614ddd916159fc565b8080614e36575b613322577f3d7ca2b1d4d92b9f80e6594957453c9542774babe911cd93d523d44fcfd019b691614e1342600b55565b60408051948552602085019190915290151590830152339180606081015b0390a3565b5062989680600c5411614de4565b3561ffff811681036104a95790565b600260045414614e64576002600455565b633ee5aeb560e01b60005260046000fd5b9190600092614e8382613bc8565b509060018060a01b03169283600052600f602052604060002083600052602052604060002090614ebc600284015460018060a01b031690565b82549260005b848110614f0f5750505050506213154e01614ede848254613c12565b90556040518381527fba40cf9b063ebedd7cfff5f8d47908f51fc0d5bfb62fdbbe8edca4666147323190602090a390565b614f2884614f206128e58486614368565b858a8a615519565b614f35575b600101614ec2565b97614f416001916143b2565b989050614f2d565b6001600160401b0316600052601260205260406000205460135481101561501557614f7381613bc8565b50916213154f83016001614f88825460ff1690565b614f9181613841565b03614fc457600293614fad61027892600260ff19825416179055565b0155600080516020615f05833981519152600080a3565b60405162461bcd60e51b8152602060048201526024808201527f526166666c65206973206e6f7420696e206472617720737461727465642073746044820152636174757360e01b6064820152608490fd5b60405162461bcd60e51b815260206004820152600a602482015269125b9d985b1a5908125160b21b6044820152606490fd5b9065ffffffffffff8091169116019065ffffffffffff821161061557565b600c549081156148e9579160009282915b8285106150995750505081146150935761508f9061531f565b5090565b50600090565b9091938481018082116106155760011c908263ffffffff6150ca614208856102796150c38a613bc8565b5001614168565b16116150e357506150da90613c04565b935b9190615076565b94506150dc565b9060038110156116bc57815460ff60e01b191660e09190911b60ff60e01b16179055565b916151289183549060031b91821b91600019901b19161790565b9055565b805490600160401b821015613c77578161514e91600161512894018155614368565b819391549060031b91821b91600019901b19161790565b90916213154c61517483613bc8565b500192835460405161518581613c5c565b6001600160a01b03841680825263ffffffff848116602084015283166040830152600060608301528654909690600160401b811015613c77576151cd91600182018155614368565b6153095781518154602084015160408501516001600160a01b039093166001600160e01b03199092169190911760a09190911b63ffffffff60a01b161760c09190911b63ffffffff60c01b161781556060909101517f16b695a62f925592d6d20c814bb52013e1fa726fabbb8579841359c2794f3051946152ae9284926152a992899261291b926152669161526182613061565b6150ea565b6001600160a01b0381166000908152600f6020526040902061528990849061291b565b54156152cc576001600160a01b03166000908152600f6020526040902090565b61512c565b6040805191825263ffffffff90921660208201529081908101614e31565b6001600160a01b03811660009081526010602052604090206152ef90849061512c565b6001600160a01b03166000908152600f6020526040902090565b634e487b7160e01b600052600060045260246000fd5b600c54811015613be9577fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701546000818152600e60205260409020546001600160a01b0390911691565b9091620186a08110615468575b61538a90836153836142a2565b9184615ae8565b5015615394575050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b156104a957604051630d0e30db60e41b81529260008460048185855af1928315610b0a5761542a94602094615453575b5060405163a9059cbb60e01b81526001600160a01b03909116600482015260248101929092529092839190829060009082906044820190565b03925af18015610b0a5761543b5750565b614d7a9060203d602011611482576114748183613c98565b80610afe600061546293613c98565b386153f1565b50620186a0615376565b6005547f000000000000000000000000000000000000000000000000000000000000000081018091116106155742106154d4577f0000000000000000000000000000000000000000000000000000000000000000420180421161061557600555565b60405162461bcd60e51b815260206004820152601860248201527f43616e6e6f74206c6f636b20617420746869732074696d6500000000000000006044820152606490fd5b93929190600554421080615535575b610fe757612bf39461556e565b503360009081527f1cb33c18d374332f5007908f8e89d7c7568d510c67ab50b033541fb034170e1c602052604090205460ff1615615528565b93909193615581846213154c8301614368565b509060046155956213154f83015460ff1690565b61559e81613841565b036157015781549560e087901c60ff16936155b885613061565b60028514615611576001600160a01b0388169788919082156156f2576155e69060a01c63ffffffff16611d2f565b956155f081613061565b1561568d575b50835460019060e01c60ff1661560b81613061565b1461561e575b5050505050505050600090565b6156346129fc6129ee89600361563a9701613d32565b91615c2a565b61564657808080615611565b805460ff60e01b1916600160e11b17905560408051918252600260208301527f897d79304faec9279753c73e7c91028343e8cc2be0ef4e5759cceafb92c429b991a4600190565b615698908285615b42565b835460ff60e01b1916600160e01b1784558787877f897d79304faec9279753c73e7c91028343e8cc2be0ef4e5759cceafb92c429b9604051806156e98b826020600191939293604081019481520152565b0390a4386155f6565b50505050505050505050600090565b60405162461bcd60e51b815260206004820152601460248201527344726177206973206e6f7420636f6d706c65746560601b6044820152606490fd5b9061577d613d09926002548060d01c806157a2575b50506002805465ffffffffffff60a01b191660a09290921b65ffffffffffff60a01b16919091179055565b600280546001600160d01b031660d09290921b6001600160d01b031916919091179055565b4211156157d457600180546001600160d01b031660309290921b6001600160d01b0319169190911790555b3880615752565b507f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec5600080a16157cd565b80600052600e602052604060002054801560001461582c57506000908152600d6020526040812054151591565b600192909150565b600254906001600160a01b038216610c0c576001600160a01b0381166001600160a01b031992909216821760025561586d906000615d13565b9081615877575090565b60008052600360205261508f907f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff615a60565b80156158e8575b6158bb8282615d13565b91826158c657505090565b60009182526003602052604090912061508f916001600160a01b031690615a60565b6002546001600160a01b038116610c0c576001600160a01b0319166001600160a01b038316176002556158b1565b6002546001600160a01b038281169261593a929091168314615977575b6000615d9c565b9081615944575090565b60008052600360205261508f907f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff615e62565b600280546001600160a01b0319169055615933565b8015806159e5575b6159d0575b6159a38282615d9c565b91826159ae57505090565b60009182526003602052604090912061508f916001600160a01b031690615e62565b600280546001600160a01b0319169055615999565b506002546001600160a01b03838116911614615994565b6001600160a01b03166000818152600e6020526040902091909155612bf390600c615a60565b805460005b818110615a3657505060009055565b80615a4360019285614368565b90549060031b1c6000528184016020526000604081205501615a27565b6000828152600182016020526040902054615ab057805490600160401b821015613c775782615a9961514e846001809601855584614368565b905580549260005201602052604060002055600190565b5050600090565b65ffffffffffff8111615acf5765ffffffffffff1690565b6306dfcc6560e41b600052603060045260245260446000fd5b604051949360209360009390615afe8689613c98565b8488526001600160401b038511613c775784936020890196601f190136883760208451940192f1903d9081615b39575b6000908286523e9190565b60009150615b2e565b6001600160a01b0382166000818152600e602052604081205492949390831580615c16575b615c025750615b78855480946140b8565b9081615bf2575050615b8990615cf8565b505b615b9442600b55565b615bb0615ba9612efa61027686015484613e04565b80926140b8565b9080615bdd575b5080615bc257505050565b6102759290920154613d0992906001600160a01b0316615369565b612f1b615bec91600a54613c12565b38615bb7565b615bfc92506159fc565b50615b8b565b63015ab34360e11b81526004839052602490fd5b50828152600d602052604081205415615b67565b600854600093926001600160a01b0392831692909116823b15615ca9576040516323b872dd60e01b81526001600160a01b03918216600482015291166024820152604481019290925290919081908390606490829084905af19182615c99575b5090615c935790565b50600190565b81615ca391613c98565b38615c8a565b8480fd5b906040519182815491828252602082019060005260206000209260005b818110615cdf575050613d0992500383613c98565b8454835260019485019487945060209093019201615cca565b612bf39080600052600e60205260006040812055600c615e62565b6000818152602081815260408083206001600160a01b038616845290915290205460ff16615ab0576000818152602081815260408083206001600160a01b03861684529091529020805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d600080a4600190565b6000818152602081815260408083206001600160a01b038616845290915290205460ff1615615ab0576000818152602081815260408083206001600160a01b03861684529091529020805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b600080a4600190565b80548015615e4c576000190190615e3a8282614368565b8154906000199060031b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b6001810191806000528260205260406000205492831515600014615efb576000198401848111610615578354600019810194908511610615576000958583615eb69761291b9503615ebc575b505050615e23565b55600190565b615ee2615edc91615ed36128e5615ef29588614368565b92839187614368565b9061510e565b8590600052602052604060002090565b55388080615eae565b5050505060009056fed453961ef428feffe24929a70412f0b1814ec58da7efbafe9d7cdc4bb965d3d6df273cb619d95419a9cd0ec88123a0538c85064229baa6363788f743fff90deba264697066735822122098430817c08be1108a50fbd5c9eb4f720f9ac9118b579a382d69d613144a080b64736f6c634300081e003360808060405234601557610781908161001b8239f35b600080fdfe6080604052600436101561001257600080fd5b6000803560e01c8063ac7dc467146102865763e3eb2dbf1461003357600080fd5b346102835760803660031901126102835760043560243590604435906064359383156102545763ffffffff8211610220576001820180921161020c5781156101d6575080831161019a5761008683610487565b93610094604051958661044f565b838552601f196100a385610487565b0136602087013760405160208101908482526553545249444560d01b6040820152602681526100d360468261044f565b51902090610116836101108160405160208101908782526513d19194d15560d21b60408201526026815261010860468261044f565b51902061049f565b936104a9565b936000805b87821061013457604051806101308b8261040f565b0390f35b6101845760009063ffffffff61015285858980808d88098b086105a7565b16895182101561017057600582901b8a01602001526001019061011b565b634e487b7160e01b83526032600452602483fd5b634e487b7160e01b600052601260045260246000fd5b60405162461bcd60e51b815260206004820152601460248201527339b634b1b29032bc31b2b2b239903237b6b0b4b760611b6044820152606490fd5b60405162461bcd60e51b815260206004820152600e60248201526d39ba30b93a24b73232bc1027a7a160911b6044820152606490fd5b634e487b7160e01b81526011600452602490fd5b60405162461bcd60e51b815260206004820152600c60248201526b0dac2f040e8dede40d0d2ced60a31b6044820152606490fd5b60405162461bcd60e51b81526020600482015260076024820152660636f756e743d360cc1b6044820152606490fd5b80fd5b50346102835760a036600319011261028357602435906044359060843560643560043584156102545763ffffffff811161022057600181018091116103fb57808610156101d6578486018087116103e757811061019a576102e685610487565b956102f4604051978861044f565b858752601f1961030387610487565b0136602089013760405160208101908482526553545249444560d01b60408201526026815261033360468261044f565b51902090610368836101108160405160208101908782526513d19194d15560d21b60408201526026815261010860468261044f565b908684155b89821061038257604051806101308d8261040f565b6103d357879063ffffffff6103a189898980808a888b01098b086105a7565b168b518210156103bf57600582901b8c01602001526001019061036d565b634e487b7160e01b8a52603260045260248afd5b634e487b7160e01b88526012600452602488fd5b634e487b7160e01b85526011600452602485fd5b634e487b7160e01b84526011600452602484fd5b602060408183019282815284518094520192019060005b8181106104335750505090565b825163ffffffff16845260209384019390920191600101610426565b90601f8019910116810190811067ffffffffffffffff82111761047157604052565b634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff81116104715760051b60200190565b8115610184570690565b91909160028311156105a05760405160208101918252630535445560e41b6040820152602481526104db60448261044f565b5190205b60016104ef60001985018361049f565b016001811461056f576001808516148015610563575b61053d575b506040516020810191825264524554525960d81b60408201526025815261053260458261044f565b519020915b916104df565b8381805b610552575060010361050a57925050565b8061055d919261049f565b80610541565b50600180821614610505565b506040516020810191825264524554525960d81b60408201526025815261059760458261044f565b51902091610537565b5060019150565b9190918215610720576001831461071757831561070e575b600c8411610705575b60006000198401805b6106f757506001811b9160001983019283116106e1576000958260011c926001841b946000198601958611935b60808a10610616575050505050505050505060001990565b8116858303908382116106e157856106e1576001821b60001981019081116106e1578089831692891c16918991938b8a956000925b8c8b851061067b57505050505016911b1788811061066e576001909901986105fe565b9850505050505050505090565b9194968580888194979b99969a16996040516020810191825260ff60f81b8d60f81b1660408201528b6041820152604181526106b860618261044f565b51902016911618169394146000146106d9576001825b960191908d9161064b565b60018b6106ce565b634e487b7160e01b600052601160045260246000fd5b906001019060011c806105d1565b600c93506105c8565b600693506105bf565b50505050600090565b60405162461bcd60e51b815260206004820152600360248201526204e3d360ec1b6044820152606490fdfea26469706673582212201c5903108e8dd90e65ba62fcde7d6328a4710f5c49c6befe53011f13bf44c12a64736f6c634300081e00335b4b8088d200445bfb44acef5ae68c0906d30def45a3f841c2e4c6f0a2b1e91452e466598c5e4408cbbaf840ea4dbd39f3e0aa1ba17a69b571858fba5698291f5246556c0ab216b5b325ad7c539bfbd1a07c768773cdf810fecd3c3375c2740700000000000000000000000000000000000000000000000000000000000000000000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000002386f26fc100000000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a420000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a4200000000000000000000000000000000000000000000000000000000000000fa000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f13400000000000000000000000000000000000000000000000000000000000000780000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a4200000000000000000000000000000000000000000000000000000000000000010000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a4200000000000000000000000000000000000000000000000000000000000000010000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42
Deployed Bytecode
0x6080604052600436101561001e575b361561001957600080fd5b6149bd565b60003560e01c806301ffc9a714610439578063022d63fb1461043457806304350ee01461042f57806307973ccf1461042a5780630aa6220b1461042557806312545c7414610420578063150b7a021461041b57806317fcb39b146104165780631eb12a411461041157806323e3fbd51461040c578063248a9ca31461040757806326e2dca214610402578063285d8eaa146103fd578063287fb78a146103f85780632f2ff15d146103f3578063342f2df3146103ee5780633446e4b4146103e957806336568abe146103e45780633a8afede146103df5780633e3d5828146103da578063441a3e70146103d557806347b9385e146103d05780634aa181f0146103cb5780634ae215c2146103c65780634beb4922146103c157806352a5f1f8146103bc5780635fba3171146103b7578063634e93da146103b2578063649a5ec7146103ad578063678375a4146103a857806367f75527146103a35780636a734c231461039e5780636aa214161461039957806377662ffc146103945780637de3fd681461038f578063802895631461038a5780638333903e1461038557806383a992b11461038057806384ef8ffc1461037b5780638da5cb5b1461037b5780638df872f41461037657806391d14854146103715780639a0b79e51461036c578063a1eda53c14610367578063a217fddf14610362578063a3246ad31461035d578063a54c120c14610358578063a69df4b514610353578063a9268b491461034e578063aac5247714610349578063ab431aa714610344578063b9d1d49b1461033f578063cc8463c81461033a578063cdd78cfc14610335578063cefc142914610330578063cf6eefb71461032b578063d0e30db014610326578063d13a03ba14610321578063d17c62b61461031c578063d4a3e86214610317578063d4f3861914610312578063d547741f1461030d578063d602b9fd14610308578063ec2dc37c14610303578063f3136678146102fe5763f505b8b90361000e57613ac7565b613953565b613817565b6137b4565b61376a565b6136ec565b613604565b61350e565b613430565b61326d565b613233565b613182565b613164565b613139565b61311b565b613078565b613022565b612e37565b612df3565b612dd5565b612d5e565b612d42565b612cdf565b612cb6565b612c60565b612bf6565b612bb3565b612ab8565b612a14565b61285e565b612422565b61237f565b612295565b611f33565b611ef7565b611b4f565b611a55565b611974565b6118ad565b6117ad565b61170b565b6116c1565b611015565b610eb4565b610e95565b610e6d565b610d8a565b610c9b565b610c3b565b610c1d565b610bbd565b610b52565b610b0f565b6109f9565b610994565b61095a565b61091e565b6108cf565b610857565b610774565b6106ce565b6106b0565b6104f1565b6104b9565b346104a95760203660031901126104a95760043563ffffffff60e01b81168091036104a9576318a4c3c360e11b811490811561047e575b506040519015158152602090f35b637965db0b60e01b811491508115610498575b5038610470565b6301ffc9a760e01b14905038610491565b600080fd5b60009103126104a957565b346104a95760003660031901126104a9576020604051620697808152f35b60609060031901126104a957600435906024359060443590565b346104a9576104ff366104d7565b610507614a1b565b61051083613bc8565b50906213154c8201916213154d81019182549060009261052e614e53565b601354881015610672579083929188945061054d849888549085614ccf565b600282015490949283916001600160a01b031682915b87831061061a57505050916213154e6105c6927ffe24c1a52828fa9764a2d2ebf1c0066647f541fa9636c315facf936ed0b3a77996959450016105a7828254613c12565b9055604051938493846040919493926060820195825260208201520152565b0390a26105d36001600455565b80549283018093116106155782905554146105ea57005b426005557f2933cccdb74a2e7c335bb68961a839d56719c5f44c65a71d0ebae64da5805dbc600080a2005b613bee565b919a909293949596978261063a91856106338f8c613c12565b9189615519565b61065a575b61064a6001916143b2565b9a0190918b979695949392610563565b9461064a6106696001926143b2565b9691505061063f565b60405162461bcd60e51b8152602060048201526011602482015270125b9d985b1a59081c9859999b19481251607a1b604482015280606481015b0390fd5b346104a95760003660031901126104a9576020600c54604051908152f35b346104a95760003660031901126104a9576106e7614a8d565b6002548060d01c80610706575b600280546001600160a01b031690555b005b42111561073857600180546001600160d01b031660309290921b6001600160d01b0319169190911790555b38806106f4565b507f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec5600080a1610731565b6001600160a01b038116036104a957565b346104a95760203660031901126104a95760043561079181610763565b610799614ae0565b6008546001600160a01b03821691908215610806576001600160a01b03198116909217600855604080516001600160a01b0393841681529290911660208301527f1e1cfbabc028aeb6656f7509a616634e3ca7cc87aa6865341b1dbddbe77e39719190819081015b0390a1005b60405162461bcd60e51b815260206004820152602360248201527f4e46542074726561737572792063616e6e6f74206265207a65726f206164647260448201526265737360e81b6064820152608490fd5b346104a95760803660031901126104a957610873600435610763565b61087e602435610763565b6064356001600160401b0381116104a957366023820112156104a95780600401356001600160401b0381116104a957369101602401116104a957604051630a85bd0160e11b815280602081015b0390f35b346104a95760003660031901126104a9576040517f0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a6001600160a01b03168152602090f35b801515036104a957565b346104a95760a03660031901126104a95761070460043560643560443560243561094783610914565b6084359361095481613bc8565b50614b8d565b346104a95760203660031901126104a957602061098a60043561097c81610763565b6001600160a01b03166157ff565b6040519081529050f35b346104a95760203660031901126104a95760206109c1600435600052600060205260016040600020015490565b604051908152f35b9181601f840112156104a9578235916001600160401b0383116104a9576020808501948460051b0101116104a957565b346104a95760603660031901126104a957600435610a1681610763565b6024356001600160401b0381116104a957610a359036906004016109c9565b90604435610a4281610763565b610a4a614ae0565b6001600160a01b039093169260005b838110610a91576040517fbdf5d7ea06ca1bc0c9d914f24222184b986a7cf5cf6dfe8c5f9eb08a19f45bc99080610801878783613ce9565b610a9c818585613c36565b3590853b156104a957604051632142170760e11b81523060048201526001600160a01b03841660248201526044810192909252600082606481838a5af1918215610b0a57600192610aef575b5001610a59565b80610afe6000610b0493613c98565b806104ae565b38610ae8565b613cb9565b346104a95760003660031901126104a957608060095460055460ff60405192818160a01c161515845260a81c161515602083015280421060408301526060820152f35b346104a957610b60366104d7565b9060135483101561067257610b7492613d63565b60405180916020820160208352815180915260206040840192019060005b818110610ba0575050500390f35b825161ffff16845285945060209384019390920191600101610b92565b346104a95760403660031901126104a957600435602435610bdd81610763565b8115610c0c5781610c07610c0261070494600052600060205260016040600020015490565b614b52565b6158aa565b631fe1e13d60e11b60005260046000fd5b346104a95760003660031901126104a9576020600a54604051908152f35b346104a95760203660031901126104a9577f9e6ff0034d37f9ece2fbd9c659a830b4544125e54557e07e79631d9284e60c8a600435610c78614ae0565b600654816006556108016040519283928360209093929193604081019481520152565b346104a95760403660031901126104a957600435602435610cbb81610763565b811580610d5c575b610cd1575b61070491614d60565b60015465ffffffffffff60a082901c1692906001600160a01b031615801590610d48575b8015610d36575b610d19576001805465ffffffffffff60a01b191690559150610cc8565b6319ca5ebb60e01b60005265ffffffffffff831660045260246000fd5b504265ffffffffffff84161015610cfc565b5065ffffffffffff831615610cf5565b1590565b50600254610d7a906001600160a01b03165b6001600160a01b031690565b6001600160a01b03821614610cc3565b60403660031901126104a9576004356001600160401b0381116104a957610db59036906004016109c9565b60243590610dc1614ae0565b610dcb8183613e04565b3403610e025760005b818110610ddd57005b80610dfc84610df7610df2600195878a613c36565b613e17565b614d8e565b01610dd4565b60405162461bcd60e51b815260206004820152603d60248201527f546f74616c206465706f73697420646f6573206e6f7420657175616c2074686560448201527f2073756d206f6620746865206465706f736974732070657220757365720000006064820152608490fd5b346104a95760203660031901126104a9576020610e8b600435613e36565b6040519015158152f35b346104a95760403660031901126104a957610704602435600435613ee8565b60403660031901126104a95760043560048110156104a9576024356001600160401b0381116104a957610eeb9036906004016109c9565b6005929192544210610fe757336000908152601160205260409020610f11908390614026565b336000908152601160205260409020610f2c90600101615a22565b610f358261401c565b81151580610fd3575b610f91575b7fefb613bc14d1a231a39f4fe36a840921cc7b01e48c25241fc92d775bcb3af966919234610f82575b610f7d60405192839233968461403e565b0390a2005b610f8c3433614d8e565b610f6c565b60005b818110610fa15750610f43565b33600090815260116020526040902060019190610fcc908301610fc5838689613c36565b3590615a60565b5001610f94565b50610fdd8261401c565b6003821415610f3e565b60405162461bcd60e51b8152602060048201526006602482015265131bd8dad95960d21b6044820152606490fd5b346104a9576101003660031901126104a95760043560643560443560243561103c83610763565b6084356001600160401b0381116104a95761105b9036906004016109c9565b60a4949194359061106b82610763565b60c4359260e4359661107c88610914565b611084614ae0565b6013548910156106725760ff6213154f61109d8b613bc8565b5001541660068110156116bc57611677578515611632576001600160a01b0382169788156115ed5783156115b5576001600160a01b0385161561157057612710861161152b5761271084116114e6576111167f000000000000000000000000000000000000000000000000000000000000007842613c12565b8810611489576008546001600160a01b03166001600160a01b0381163014159081611414575b506113b4579790899796959493929161115489613bc8565b50878155600181018990556002810180546001600160a01b0319166001600160a01b0386161790559960038b019060005b8681106112515750506102748b018590556102758b0180546001600160a01b0319166001600160a01b038881169182179092556102768d018990556040805160008152602081018c90529081018c905291861660608301526080820187905260a082015260c081018890528a90611232906213154f908e9084907fb29e735fcfbbcb24f5742786b6f34964d70f3b0ee0b32d45f7411961f6e0c640908060e081015b0390a2015460ff1690565b9061123c82613841565b600080516020615f05833981519152600080a3005b908080939495969798999a9b9c5090611312575b6112b857806112a661128261127d6001948b8a613c36565b614e44565b61128c8387613d32565b90919061ffff8084549260031b9316831b921b1916179055565b01908c9a999897969594939291611185565b60405162461bcd60e51b815260206004820152602c60248201527f4e465420547265617375727920646f6573206e6f7420686f6c6420616c6c204560448201526b524337323120746f6b656e7360a01b6064820152608490fd5b5061134a602061132661127d848b8a613c36565b6040516331a9108f60e11b815261ffff909116600482015291829081906024820190565b0381885afa908115610b0a57600091611386575b50600854611374906001600160a01b0316610d6e565b6001600160a01b039091161415611265565b6113a7915060203d81116113ad575b61139f8183613c98565b810190613e21565b3861135e565b503d611395565b60405162461bcd60e51b815260206004820152603260248201527f4e4654205472656173757279206d75737420617070726f7665204d696e7468756044820152716220746f207472616e73666572204e46547360701b6064820152608490fd5b60405163e985e9c560e01b81526001600160a01b0391909116600482015230602482015290506020816044818d5afa908115610b0a5760009161145a575b50153861113c565b61147c915060203d602011611482575b6114748183613c98565b810190614253565b38611452565b503d61146a565b60405162461bcd60e51b815260206004820152602f60248201527f526166666c65206d757374206265206f70656e20666f72206174206c6561737460448201526e081b5a5b9a5b5d5b481c195c9a5bd9608a1b6064820152608490fd5b60405162461bcd60e51b815260206004820152601c60248201527f546f6b656e204944732065786365656473206d6178207072697a6573000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601860248201527f4665652069732067726561746572207468616e203130302500000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601f60248201527f50726f6a6563742074726561737572792063616e6e6f74206265207a65726f006044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f4d7573742068617665207072697a657360801b6044820152606490fd5b60405162461bcd60e51b815260206004820152601a60248201527f4e465420616464726573732063616e6e6f74206265207a65726f0000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601860248201527f5469636b6574207072696365206d757374206265203e203000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820181905260248201527f526166666c65206973206e6f7420696e207072652064726177207374617475736044820152606490fd5b61304b565b346104a95760203660031901126104a9576107046004356116e1336157ff565b9050613ee8565b60609060031901126104a95760043561170081610763565b906024359060443590565b346104a957611719366116e8565b90611722614e53565b6013548110156106725761173592614e75565b15611744576107046001600455565b60405162461bcd60e51b815260206004820152602a60248201527f4e6f207072697a657320666f72207573657220696e20726166666c6520746f206044820152696469737472696275746560b01b6064820152608490fd5b6001600160401b038116036104a957565b346104a95760603660031901126104a9576004356117ca8161179c565b6117d5602435610763565b6044357f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f1346001600160a01b031680156118685733036118175761070491614f49565b60405162461bcd60e51b815260206004820152602360248201527f4f6e6c7920456e74726f70792063616e2063616c6c20746869732066756e637460448201526234b7b760e91b6064820152608490fd5b60405162461bcd60e51b815260206004820152601760248201527f456e74726f70792061646472657373206e6f74207365740000000000000000006044820152606490fd5b346104a95760203660031901126104a9576004356118c9614ae0565b6013548110156106725760ff6213154f6118e283613bc8565b5001541660068110156116bc5760041461192f57806119106001611907600594613bc8565b50600019910155565b61123c6213154f61192083613bc8565b5001805460ff19166005179055565b60405162461bcd60e51b815260206004820181905260248201527f43616e6e6f742063616e63656c206120636f6d706c6574656420726166666c656044820152606490fd5b346104a95760203660031901126104a95760043561199181610763565b611999614a8d565b7f3377dc44241e779dd06afab5b788a35ca5f3b778836e2990bdb26a2a4b2e5ed660206119d66119c842615ab7565b6119d06144e7565b90615047565b600180546001600160d01b031981166001600160a01b039690961695861760a084811b65ffffffffffff60a01b169190911790925565ffffffffffff911c16611a2b575b65ffffffffffff60405191168152a2005b7f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a9605109600080a1611a1a565b346104a95760203660031901126104a95760043565ffffffffffff8116908181036104a957611a82614a8d565b611a8b42615ab7565b9165ffffffffffff611a9b6144e7565b1680821115611b1557507ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b9265ffffffffffff826206978080611ae8951091180262069780181690615047565b90611af3828261573d565b6040805165ffffffffffff928316815292909116602083015281908101610801565b0365ffffffffffff8111610615577ff1038c18cf84a56e432fdbfaf746924b7ea511dfe03a6506a0ceba4888788d9b92611ae89190615047565b346104a95760403660031901126104a957600435602435906013548110156106725762131550611b7e82613bc8565b5001805480611e9a57504290555b611b94614a1b565b8115611e6457611ba381613bc8565b506213154f8101906003611bb8835460ff1690565b611bc181613841565b03611e10576213154a810154918215611dde57611c1d9482610274610278940154808610600014611dd55750600085925b6213154c830192611c598454978893611c0b85896140b8565b8082108714611dcb57509b8c9a6140a9565b9201546040519b8c94859463ac7dc46760e01b86526004860190949360069360809360a084019784526020840152604083015260608201520152565b03817f0000000000000000000000007bcbf44010f48807d7b2568e9dc3db1bb7c55c876001600160a01b03165afa968715610b0a57600097611da8575b5060005b858110611d065750917f1fd2691832f2c75387438682910304c91d73ece89b0e6123fe3813ddad1ed70d959691610f7d935414611cdf575b506040519384938461414c565b805460ff19166004179055600486600080516020615f05833981519152600080a338611cd2565b600190611d826000828b63ffffffff611d38611d2f611d258585613d4f565b5163ffffffff1690565b63ffffffff1690565b03611d885750611d489089613c12565b8a7f66a8c5b89438eb03696fbf4fae05d99dcfa81867a18c352b258b5de0aea1c246600080a35b611d7c611d25848d613d4f565b8a615165565b01611c9a565b611da39250611d25611d9d92611d2f92613d4f565b8a615065565b611d6f565b611dc491973d8091833e611dbc8183613c98565b8101906140c5565b9538611c96565b90509b8c9a6140a9565b60009092611bf2565b60405162461bcd60e51b815260206004820152600a6024820152694e6f207469636b65747360b01b6044820152606490fd5b60405162461bcd60e51b815260206004820152602660248201527f526166666c65206e6f7420726561647920666f722077696e6e65722067656e656044820152653930ba34b7b760d11b6064820152608490fd5b60405162461bcd60e51b815260206004820152600e60248201526d0426174636853697a6520697320360941b6044820152606490fd5b9050600b5410611b8c5760405162461bcd60e51b8152806106ac6004820161405f565b906020808351928381520192019060005b818110611edb5750505090565b825163ffffffff16845260209384019390920191600101611ece565b346104a957611f05366104d7565b601392919254821015610672576108cb92611f1f92614186565b604051918291602083526020830190611ebd565b346104a95760403660031901126104a9576004356024356013548210156106725762131550611f6183613bc8565b500180548061227257504290555b611f77614a1b565b611f8082613bc8565b50906213154f8201916002611f96845460ff1690565b611f9f81613841565b0361221957611fbc611fb447600a54906140b8565b825490614233565b63fffffffe1061218f57600c54926102798201621315498301611fe181549586613c12565b958087101561215b575b508590556213154a83019081549354855b87811061206957887fd5be8ab785440155ef7ce08e886cd5ab029663ba21e3c3aa702f68a30fd7799f60038a610f7d8b8b61203b8c828d555460ff1690565b9361204585613841565b60408051938452602084019190915282015292909114606083015281906080820190565b886120738261531f565b91908892612081828461486e565b61210f575b50907fc82b78cab3e074fc5b75f2f9e68a9d8aadd66032fac0293513c12d5925c470268493926120df8b63ffffffff6120c16001998c614168565b919092169063ffffffff8084549260031b9316831b921b1916179055565b6120e9848c6140b8565b60408051878152602081019690965285015260a086901b8690031692606090a301611ffc565b6121506001959493929a61214a611d2f611d2f8a7fc82b78cab3e074fc5b75f2f9e68a9d8aadd66032fac0293513c12d5925c4702696614233565b90613c12565b999192939450612086565b6213154b8501819055835460ff1916600317845595508590600388600080516020615f05833981519152600080a390611feb565b60405162461bcd60e51b815260206004820152605660248201527f546f74616c207469636b6574732065786365656473206d6178696d756d20706f60448201527f737369626c65207469636b6574733a2063616e6e6f7420656e756d65726174656064820152751030ba103a3434b9903a34b1b5b2ba10383934b1b29760511b608482015260a490fd5b60405162461bcd60e51b815260206004820152602b60248201527f526166666c65206973206e6f7420696e2072616e646f6d6e657373207265636560448201526a697665642073746174757360a81b6064820152608490fd5b9050600b5410611f6f5760405162461bcd60e51b8152806106ac6004820161405f565b346104a95760203660031901126104a9576004356122b1614e53565b6009546001600160a01b03161561233a57600a549081156122f5576122eb916122da6000600a55565b6009546001600160a01b0316615369565b6107046001600455565b60405162461bcd60e51b815260206004820152601c60248201527f4e6f20706c6174666f726d206665657320746f207769746864726177000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601d60248201527f506c6174666f726d20666565207472656173757279206e6f74207365740000006044820152606490fd5b346104a95760603660031901126104a9576123f160206004356123a181610763565b602435906044356123b181610763565b6123b9614ae0565b60405163a9059cbb60e01b81526001600160a01b03909116600482015260248101929092529092839190829060009082906044820190565b03926001600160a01b03165af18015610b0a5761240a57005b6107049060203d602011611482576114748183613c98565b60403660031901126104a95760243560043561243d82610914565b6013548110156106725760ff60095460a01c166127645761245c614a1b565b61246581613bc8565b50916001830154421061271f576213154f830190612484825460ff1690565b61248d81613841565b611677578061270d575b6112b857805460ff191660011790556124ae615472565b6040516341025b3d60e11b81527f000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f1346001600160a01b031690602081600481855afa8015610b0a576001600160801b03916000916126de575b50169081341061268d5781341161261d575b602090600460405180948193637b43155d60e01b83525af1908115610b0a576000916125ee575b506001600160401b038116156125a9576125938161257a849361027760019701906001600160401b03166001600160401b0319825416179055565b6001600160401b03166000526012602052604060002090565b55600080516020615f05833981519152600080a3005b60405162461bcd60e51b815260206004820152601760248201527f496e76616c69642073657175656e6365206e756d6265720000000000000000006044820152606490fd5b612610915060203d602011612616575b6126088183613c98565b8101906142e7565b3861253f565b503d6125fe565b600080808061262c86346140b8565b335af16126376142b7565b506125185760405162461bcd60e51b8152602060048201526024808201527f4661696c656420746f20726566756e642065786365737320656e74726f70792060448201526318dbdcdd60e21b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f496e73756666696369656e742076616c756520666f7220656e74726f707920636044820152621bdcdd60ea1b6064820152608490fd5b612700915060203d602011612706575b6126f88183613c98565b810190614268565b38612506565b503d6126ee565b5061271a610d5883613e36565b612497565b60405162461bcd60e51b815260206004820152601860248201527f526166666c65206973206e6f742079657420636c6f73656400000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f111c985ddcc8185c99481c185d5cd95960821b6044820152606490fd5b906020808351928381520192019060005b8181106127ba5750505090565b82518452602093840193909201916001016127ad565b602081016020825282518091526040820191602060408360051b8301019401926000915b83831061280357505050505090565b9091929394602080600192603f1985820301865288519081518152848060a01b03838301511683820152606080612849604085015160806040860152608085019061279c565b930151910152970193019301919392906127f4565b346104a95761289261286f366116e8565b6001600160a01b0383166000908152601060205260409020919391549084614ccf565b61289b816142fc565b916000935b8285106128b557604051806108cb86826127d0565b6001600160a01b03821660009081526010602052604090206128ef906128e5906128df8885613c12565b90614368565b90549060031b1c90565b946128f986613bc8565b509561292a8161291b8660018060a01b0316600052600f602052604060002090565b90600052602052604060002090565b549661293588614380565b600382019860005b8181106129ae575050600194959697985061298485612965600285015460018060a01b031690565b93015492612971613cfa565b9485526001600160a01b03166020850152565b604083015260608201526129988288613d4f565b526129a38187613d4f565b5001939291906128a0565b80612a036129fc6129ee8e6129e86128e56001978f8d61291b6129e39260018060a01b0316600052600f602052604060002090565b614368565b90613d32565b905461ffff9160031b1c1690565b61ffff1690565b612a0d8286613d4f565b520161293d565b346104a95760203660031901126104a957600435612a30614ae0565b600754906103e88111612a7d5760078190556040805192835260208301919091527f91cf3aa4a4860dcd0e8a1db910306400c8432212e7f52c5e3dfd3d0b79f8277a919081908101610801565b60405162461bcd60e51b81526020600482015260136024820152724665652065786365656473206d6178696d756d60681b6044820152606490fd5b346104a957612ac6366104d7565b612ad1929192614e53565b60135482101561067257612ae482613bc8565b50926213154c840154811015612b785760018060a01b03600285015416600554421080612b3f575b610fe757612b1a938561556e565b612b28576107046001600455565b6213154e01612b3781546143b2565b9055386122eb565b503360009081527f1cb33c18d374332f5007908f8e89d7c7568d510c67ab50b033541fb034170e1c602052604090205460ff1615612b0c565b60405162461bcd60e51b8152602060048201526013602482015272092dcecc2d8d2c840e0e4d2f4ca40d2dcc8caf606b1b6044820152606490fd5b346104a95760003660031901126104a9576002546040516001600160a01b039091168152602090f35b604090612bf393928152816020820152019061279c565b90565b346104a95760203660031901126104a957600435612c1381610763565b6001600160a01b031660008181526011602052604090205460ff169060048210156116bc576000526011602052612c506001604060002001615cad565b906108cb60405192839283612bdc565b346104a95760403660031901126104a957602060ff612caa602435600435612c8782610763565b6000526000845260406000209060018060a01b0316600052602052604060002090565b54166040519015158152f35b346104a95760003660031901126104a9576009546040516001600160a01b039091168152602090f35b346104a95760003660031901126104a9576002548060d01c9081151580612d38575b15612d2e5760a01c65ffffffffffff165b6040805165ffffffffffff928316815292909116602083015290f35b5050600080612d12565b5042821015612d01565b346104a95760003660031901126104a957602060405160008152f35b346104a95760203660031901126104a9576004356000526003602052612d876040600020615cad565b60405180916020820160208352815180915260206040840192019060005b818110612db3575050500390f35b82516001600160a01b0316845285945060209384019390920191600101612da5565b346104a95760003660031901126104a9576020601354604051908152f35b346104a95760003660031901126104a957612e0c614ae0565b426005557f7e80c098f669ba3a098abdc0e1112d3229b26c098a48dc2701b7802336f5845c600080a1005b346104a957612e5d612e48366104d7565b919290612e53614ae0565b600c549084614ccf565b805b612e6557005b80830180841161061557600019810190811161061557612e849061531f565b9060065482101561301b57612e97614e53565b6005544210610fe757612ea981613c1f565b808311612fdd5782612eba916140b8565b80612f7757600080516020615f2583398151915291612ee9612ee46001600160a01b038316610d6e565b615cf8565b505b612f02612efa60075486613e04565b612710900490565b612f20612f1b612f1283886140b8565b92600a54613c12565b600a55565b8681612f66575b5050612f3242600b55565b6040805194855260208501929092526001600160a01b031692a2612f566001600455565b8015610615576000190180612e5f565b612f709183615369565b3886612f27565b6006548110612fa05781612f9a82600080516020615f25833981519152946159fc565b50612eeb565b60405162461bcd60e51b815260206004820152601560248201527442616c616e63652062656c6f77206d696e696d756d60581b6044820152606490fd5b60405162461bcd60e51b8152602060048201526016602482015275416d6f756e7420657863656564732062616c616e636560501b6044820152606490fd5b5050612f56565b346104a95760003660031901126104a9576008546040516001600160a01b039091168152602090f35b634e487b7160e01b600052602160045260246000fd5b600311156116bc57565b9060038210156116bc5752565b346104a957613086366104d7565b906013548310156106725761309a9261440f565b60405180916020820160208352815180915260206040840192019060005b8181106130c6575050500390f35b919350916020608060019261310d60608851868060a01b03815116845263ffffffff86820151168685015263ffffffff60408201511660408501520151606083019061306b565b0194019101918493926130b8565b346104a95760003660031901126104a9576020600654604051908152f35b346104a95760003660031901126104a95760206131546144e7565b65ffffffffffff60405191168152f35b346104a95760003660031901126104a9576020600754604051908152f35b346104a95760003660031901126104a9576001546001600160a01b0316330361321e5760015460a081901c65ffffffffffff16906001600160a01b031681158015613214575b6131ff576002546131ec91906131e6906001600160a01b0316615916565b50615834565b50600180546001600160d01b0319169055005b506319ca5ebb60e01b60005260045260246000fd5b50428210156131c8565b636116401160e11b6000523360045260246000fd5b346104a95760003660031901126104a957600154604080516001600160a01b038316815260a09290921c65ffffffffffff16602083015290f35b60003660031901126104a9576005544210610fe75760ff60095460a81c166133f55733156133b0573415613375576132ad346132a833613c1f565b613c12565b6006548110612fa0576132c081336159fc565b908180613367575b613322576132d542600b55565b7f3d7ca2b1d4d92b9f80e6594957453c9542774babe911cd93d523d44fcfd019b66040518061331d339533953484919260409194936060840195845260208401521515910152565b0390a3005b60405162461bcd60e51b815260206004820152601a60248201527f4d617820706c6174666f726d20757365727320726561636865640000000000006044820152606490fd5b5062989680600c54116132c8565b60405162461bcd60e51b815260206004820152601360248201527243616e6e6f74206465706f736974207a65726f60681b6044820152606490fd5b60405162461bcd60e51b815260206004820152601b60248201527f557365722063616e6e6f74206265207a65726f206164647265737300000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601360248201527211195c1bdcda5d1cc8185c99481c185d5cd959606a1b6044820152606490fd5b346104a95760403660031901126104a95760043561344d81610763565b602435613458614e53565b6001600160a01b0382166000818152601060205260408120549093849391925b8385106134d457851561348f576107046001600455565b60405162461bcd60e51b815260206004820181905260248201527f4e6f207072697a657320666f72207573657220746f20646973747269627574656044820152606490fd5b909192939461350360019183600052601060205261214a866134fd6128e58b6040600020614368565b87614e75565b950193929190613478565b346104a95760203660031901126104a95760043561352b81610914565b613533614ae0565b60095460a01c60ff16151581151514613599576009805460ff60a01b191682151560a01b60ff60a01b161790557fd42e3119c2424e39164970a3305ba51bd6f674514318dfb7e5c1c2bc3fb0f03e90610801905b60405190151581529081906020820190565b60405162461bcd60e51b815260206004820152603760248201527f4472617773207061757365642073746174757320697320616c7265616479207360448201527f657420746f2074686520646573697265642076616c75650000000000000000006064820152608490fd5b346104a95760203660031901126104a95760043561362181610914565b613629614ae0565b60095460a81c60ff16151581151514613681576009805460ff60a81b191682151560a81b60ff60a81b161790557f9cc2044889457670dcf1b4f002e64d84b8507017b30f77aab31d88416a23c4cd9061080190613587565b60405162461bcd60e51b815260206004820152603a60248201527f4465706f73697473207061757365642073746174757320697320616c7265616460448201527f792073657420746f2074686520646573697265642076616c75650000000000006064820152608490fd5b346104a95760e03660031901126104a95760443560243560043561370f83610763565b606435916001600160401b0383116104a9576108cb9361373661375a9436906004016109c9565b906084359261374484610763565b60a4359460c4359661375588610914565b614520565b6040519081529081906020820190565b346104a95760403660031901126104a95760043560243561378a81610763565b8115610c0c57816137af610c0261070494600052600060205260016040600020015490565b61598c565b346104a95760003660031901126104a9576137cd614a8d565b600180546001600160d01b0319811690915560a01c65ffffffffffff166137f057005b7f8886ebfc4259abdbc16601dd8fb5678e54878f47b3c34836cfc51154a9605109600080a1005b346104a95760403660031901126104a9576020610e8b60243560043561383c82610763565b61486e565b600611156116bc57565b9060068210156116bc5752565b602060408183019282815284518094520192019060005b81811061387c5750505090565b90919260206101c06001926139486101a08851805184528581015186850152604081015160408501526138be6060820151606086019060018060a01b03169052565b6080818101519085015260a0808201516001600160a01b03169085015260c081015160c08501526138ff60e082015160e08601906001600160401b03169052565b61010081015161010085015261012081015161012085015261014081015161014085015261016081015161016085015261018081015161018085015201516101a083019061384b565b01940192910161386f565b346104a95760403660031901126104a9576004356139776024356013549083614ccf565b90613981826148f9565b9060005b83811061399a57604051806108cb8582613858565b6139a48183613c12565b6139ad90613bc8565b506139b88284613c12565b815460018301546002840154929391926001600160a01b03166102748301546102758401546001600160a01b03166102768501546102778601546001600160401b0316906102788701549262131549880154946213154a890154966213154c8a0154986213154e8b01549a6213154f0154613a339060ff1690565b9b613a3c613d0b565b9d8e5260208e015260408d01526001600160a01b031660608c015260808b01526001600160a01b031660a08a015260c08901526001600160401b031660e0880152610100870152610120860152610140850152610160840152610180830152613aa9906101a083016149b1565b613ab38285613d4f565b52613abe8184613d4f565b50600101613985565b346104a95760203660031901126104a957600435613ae481610763565b613aec614ae0565b6009546001600160a01b03821691908215613b58576001600160a01b03198116909217600955604080516001600160a01b0393841681529290911660208301527f812b3ab9a36a8650971353f9b65095ff5cd4b2104e27b9c9eff6b32757516864919081908101610801565b60405162461bcd60e51b815260206004820152602c60248201527f506c6174666f726d206665652074726561737572792063616e6e6f742062652060448201526b7a65726f206164647265737360a01b6064820152608490fd5b634e487b7160e01b600052603260045260246000fd5b601354811015613be957601360005262131551602060002091020190600090565b613bb2565b634e487b7160e01b600052601160045260246000fd5b906001820180921161061557565b9190820180921161061557565b613c31906001600160a01b03166157ff565b905090565b9190811015613be95760051b0190565b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b03821117613c7757604052565b613c46565b6101c081019081106001600160401b03821117613c7757604052565b90601f801991011681019081106001600160401b03821117613c7757604052565b6040513d6000823e3d90fd5b81835290916001600160fb1b0383116104a95760209260051b809284830137010190565b916020612bf3938181520191613cc5565b60405190613d09608083613c98565b565b60405190613d096101c083613c98565b6001600160401b038111613c775760051b60200190565b919091612710831015613be957601e908360041c019260011b1690565b8051821015613be95760209160051b010190565b613d6c90613bc8565b50916102748301548015613dfb57613d849183614ccf565b613d8d81613d1b565b92613d9b6040519485613c98565b818452601f19613daa83613d1b565b0136602086013760030160005b828110613dc5575050505090565b8084019081851161061557613df5613de26129ee60019486613d32565b613dec8389613d4f565b9061ffff169052565b01613db7565b50505050606090565b8181029291811591840414171561061557565b35612bf381610763565b908160209103126104a95751612bf381610763565b613e3f90613bc8565b50600281015461027482015460085490926000926001600160a01b0390811692600390920191613e6f9116610d6e565b925b848110613e82575050505050600190565b613e9460206113266129ee8486613d32565b0381875afa8015610b0a578591600091613eca575b506001600160a01b031603613ec057600101613e71565b5050505050600090565b613ee2915060203d81116113ad5761139f8183613c98565b38613ea9565b908115613fe057613ef7614e53565b6005544210610fe757613f0933613c1f565b808311612fdd5782613f1a916140b8565b9081613fb357600080516020615f2583398151915291613f9591613f40612ee433610d6e565b505b613f51612efa60075487613e04565b90613f6b612f1b613f6284896140b8565b93600a54613c12565b81613fa2575b5050613f7c42600b55565b6040805194855260208501919091523393918291820190565b0390a2613d096001600455565b613fac9133615369565b3880613f71565b6006548210612fa05781613f9591613fda600080516020615f2583398151915294336159fc565b50613f42565b60405162461bcd60e51b815260206004820152601460248201527343616e6e6f74207769746864726177207a65726f60601b6044820152606490fd5b600411156116bc57565b9060048110156116bc5760ff80198354169116179055565b929060048110156116bc5783604091612bf395528160208201520191613cc5565b60809060208152602960208201527f42616c616e6365732068617665206368616e67656420647572696e6720647261604082015268772070726f6365737360b81b60608201520190565b60001981019190821161061557565b9190820391821161061557565b6020818303126104a9578051906001600160401b0382116104a957019080601f830112156104a9578151906140f982613d1b565b926141076040519485613c98565b82845260208085019360051b8201019182116104a957602001915b81831061412f5750505090565b825163ffffffff811681036104a957815260209283019201614122565b612bf39392606092825260208201528160408201520190611ebd565b91909162989680831015613be957601c908360031c019260021b1690565b61418f90613bc8565b50916213154b61027984019301548015613dfb576141ad9183614ccf565b6141b681613d1b565b926141c46040519485613c98565b818452601f196141d383613d1b565b0136602086013760005b8281106141eb575050505090565b808401908185116106155761422d61421861420860019486614168565b905463ffffffff9160031b1c1690565b6142228389613d4f565b9063ffffffff169052565b016141dd565b811561423d570490565b634e487b7160e01b600052601260045260246000fd5b908160209103126104a95751612bf381610914565b908160209103126104a957516001600160801b03811681036104a95790565b6001600160401b038111613c7757601f01601f191660200190565b604051906142b1602083613c98565b60008252565b3d156142e2573d906142c882614287565b916142d66040519384613c98565b82523d6000602084013e565b606090565b908160209103126104a95751612bf38161179c565b9061430682613d1b565b6143136040519182613c98565b8281528092614324601f1991613d1b565b019060005b82811061433557505050565b60209060405161434481613c5c565b60008152600083820152606060408201526000606082015282828501015201614329565b8054821015613be95760005260206000200190600090565b9061438a82613d1b565b6143976040519182613c98565b82815280926143a8601f1991613d1b565b0190602036910137565b60001981146106155760010190565b906040516143ce81613c5c565b91546001600160a01b038116835260a081901c63ffffffff908116602085015260c082901c16604084015260e01c60ff168260038210156116bc5760600152565b61441f6213154c91939293613bc8565b50019081548015613dfb576144349184614ccf565b9061443e82613d1b565b9261444c6040519485613c98565b828452601f1961445b84613d1b565b0160005b8181106144b457505060005b838110614479575050505090565b8061449861449261448c60019486613c12565b86614368565b506143c1565b6144a28288613d4f565b526144ad8187613d4f565b500161446b565b6020906040516144c381613c5c565b6000815260008382015260006040820152600060608201528282890101520161445f565b6002548060d01c8015159081614516575b501561450c5760a01c65ffffffffffff1690565b5060015460d01c90565b90504211386144f8565b3360009081527f6c9fbe396e57de500d525cbaec4c426195022646e1d232f522c0ab4b33e90899602052604090205493979691959094919390929060ff16156148355760135497600160401b891015613c77576001890160135561458389613bc8565b50508515611632576001600160a01b0382169788156115ed5783156115b5576001600160a01b0385161561157057612710861161152b5761271084116114e6576145ed7f000000000000000000000000000000000000000000000000000000000000007842613c12565b8810611489576008546001600160a01b03166001600160a01b03811630141590816147d0575b506113b4579790899796959493929161462b89613bc8565b50878155600181018990556002810180546001600160a01b0319166001600160a01b0386161790559960038b019060005b8681106147245750505050506102748801829055610275880180546001600160a01b0319166001600160a01b039485169081179091556102768901859055604080516001815260208101979097528601969096529091166060840152608083015260a082019290925260c0810191909152614704926213154f9290917fb29e735fcfbbcb24f5742786b6f34964d70f3b0ee0b32d45f7411961f6e0c640908060e08101611227565b61470d81613841565b81600080516020615f05833981519152600080a390565b908080939495969798999a9b9c5090614762575b6112b8578061475061128261127d6001948b8a613c36565b01908c9a99989796959493929161465c565b50614776602061132661127d848b8a613c36565b0381885afa908115610b0a576000916147b2575b506008546147a0906001600160a01b0316610d6e565b6001600160a01b039091161415614738565b6147ca915060203d81116113ad5761139f8183613c98565b3861478a565b60405163e985e9c560e01b81526001600160a01b0391909116600482015230602482015290506020816044818d5afa908115610b0a57600091614816575b501538614613565b61482f915060203d602011611482576114748183613c98565b3861480e565b63e2517d3f60e01b600052336004527f5b4b8088d200445bfb44acef5ae68c0906d30def45a3f841c2e4c6f0a2b1e91460245260446000fd5b9060018060a01b0316600052601160205260406000209060ff8254166148938161401c565b80156148f1576148a28161401c565b600381146148e957806148b660019261401c565b146148d257600090815260029190910160205260409020541590565b600090815260029091016020526040902054151590565b505050600090565b505050600190565b9061490382613d1b565b6149106040519182613c98565b8281528092614921601f1991613d1b565b019060005b82811061493257505050565b60209060405161494181613c7c565b60008152600083820152600060408201526000606082015260006080820152600060a0820152600060c0820152600060e08201526000610100820152600061012082015260006101408201526000610160820152600061018082015260006101a082015282828501015201614926565b60068210156116bc5752565b60405162461bcd60e51b815260206004820152603060248201527f43616e6e6f74207265636569766520455448206469726563746c792c2075736560448201526f2070617961626c65206d6574686f647360801b6064820152608490fd5b3360009081527f1cb33c18d374332f5007908f8e89d7c7568d510c67ab50b033541fb034170e1c602052604090205460ff1615614a5457565b63e2517d3f60e01b600052336004527f52e466598c5e4408cbbaf840ea4dbd39f3e0aa1ba17a69b571858fba5698291f60245260446000fd5b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff1615614ac657565b63e2517d3f60e01b60005233600452600060245260446000fd5b3360009081527f0790b0aaeb4157b4822815d95bf3d9318a1561c876c520e6cca704be6f4d63a6602052604090205460ff1615614b1957565b63e2517d3f60e01b600052336004527f5246556c0ab216b5b325ad7c539bfbd1a07c768773cdf810fecd3c3375c2740760245260446000fd5b60008181526020818152604080832033845290915290205460ff1615614b755750565b63e2517d3f60e01b6000523360045260245260446000fd5b949290939194614b9b614e53565b60135485101561067257614bb96000966213154c8301549084614ccf565b6002820154600095919491906001600160a01b03166000915b868310614c855750505080614c7d575b614c2a577ffe24c1a52828fa9764a2d2ebf1c0066647f541fa9636c315facf936ed0b3a779936213154e614c1c92016105a7828254613c12565b0390a290613d096001600455565b60405162461bcd60e51b815260206004820152602560248201527f4e6f207072697a657320746f206469737472696275746520696e2074686973206044820152640c4c2e8c6d60db1b6064820152608490fd5b508315614be2565b909198614c9e82614c968c89613c12565b858c89615519565b614cb7575b614cae6001916143b2565b99019190614bd2565b96614cae614cc66001926143b2565b98915050614ca3565b9082821015614d0b5780158015614cf9575b15614cf3575081039081116106155790565b91505090565b50808201808311610615578310614ce1565b60405162461bcd60e51b815260206004820152602760248201527f537461727420696e646578206d757374206265206c657373207468616e20746860448201526619481d1bdd185b60ca1b6064820152608490fd5b90336001600160a01b03821603614d7d57614d7a9161598c565b50565b63334bd91960e11b60005260046000fd5b906005544210610fe75760ff60095460a81c166133f5576001600160a01b0382169182156133b057811561337557614dc9826132a883613c1f565b906006548210612fa05781614ddd916159fc565b8080614e36575b613322577f3d7ca2b1d4d92b9f80e6594957453c9542774babe911cd93d523d44fcfd019b691614e1342600b55565b60408051948552602085019190915290151590830152339180606081015b0390a3565b5062989680600c5411614de4565b3561ffff811681036104a95790565b600260045414614e64576002600455565b633ee5aeb560e01b60005260046000fd5b9190600092614e8382613bc8565b509060018060a01b03169283600052600f602052604060002083600052602052604060002090614ebc600284015460018060a01b031690565b82549260005b848110614f0f5750505050506213154e01614ede848254613c12565b90556040518381527fba40cf9b063ebedd7cfff5f8d47908f51fc0d5bfb62fdbbe8edca4666147323190602090a390565b614f2884614f206128e58486614368565b858a8a615519565b614f35575b600101614ec2565b97614f416001916143b2565b989050614f2d565b6001600160401b0316600052601260205260406000205460135481101561501557614f7381613bc8565b50916213154f83016001614f88825460ff1690565b614f9181613841565b03614fc457600293614fad61027892600260ff19825416179055565b0155600080516020615f05833981519152600080a3565b60405162461bcd60e51b8152602060048201526024808201527f526166666c65206973206e6f7420696e206472617720737461727465642073746044820152636174757360e01b6064820152608490fd5b60405162461bcd60e51b815260206004820152600a602482015269125b9d985b1a5908125160b21b6044820152606490fd5b9065ffffffffffff8091169116019065ffffffffffff821161061557565b600c549081156148e9579160009282915b8285106150995750505081146150935761508f9061531f565b5090565b50600090565b9091938481018082116106155760011c908263ffffffff6150ca614208856102796150c38a613bc8565b5001614168565b16116150e357506150da90613c04565b935b9190615076565b94506150dc565b9060038110156116bc57815460ff60e01b191660e09190911b60ff60e01b16179055565b916151289183549060031b91821b91600019901b19161790565b9055565b805490600160401b821015613c77578161514e91600161512894018155614368565b819391549060031b91821b91600019901b19161790565b90916213154c61517483613bc8565b500192835460405161518581613c5c565b6001600160a01b03841680825263ffffffff848116602084015283166040830152600060608301528654909690600160401b811015613c77576151cd91600182018155614368565b6153095781518154602084015160408501516001600160a01b039093166001600160e01b03199092169190911760a09190911b63ffffffff60a01b161760c09190911b63ffffffff60c01b161781556060909101517f16b695a62f925592d6d20c814bb52013e1fa726fabbb8579841359c2794f3051946152ae9284926152a992899261291b926152669161526182613061565b6150ea565b6001600160a01b0381166000908152600f6020526040902061528990849061291b565b54156152cc576001600160a01b03166000908152600f6020526040902090565b61512c565b6040805191825263ffffffff90921660208201529081908101614e31565b6001600160a01b03811660009081526010602052604090206152ef90849061512c565b6001600160a01b03166000908152600f6020526040902090565b634e487b7160e01b600052600060045260246000fd5b600c54811015613be9577fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701546000818152600e60205260409020546001600160a01b0390911691565b9091620186a08110615468575b61538a90836153836142a2565b9184615ae8565b5015615394575050565b7f0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a6001600160a01b031691823b156104a957604051630d0e30db60e41b81529260008460048185855af1928315610b0a5761542a94602094615453575b5060405163a9059cbb60e01b81526001600160a01b03909116600482015260248101929092529092839190829060009082906044820190565b03925af18015610b0a5761543b5750565b614d7a9060203d602011611482576114748183613c98565b80610afe600061546293613c98565b386153f1565b50620186a0615376565b6005547f000000000000000000000000000000000000000000000000000000000000007881018091116106155742106154d4577f0000000000000000000000000000000000000000000000000000000000002a30420180421161061557600555565b60405162461bcd60e51b815260206004820152601860248201527f43616e6e6f74206c6f636b20617420746869732074696d6500000000000000006044820152606490fd5b93929190600554421080615535575b610fe757612bf39461556e565b503360009081527f1cb33c18d374332f5007908f8e89d7c7568d510c67ab50b033541fb034170e1c602052604090205460ff1615615528565b93909193615581846213154c8301614368565b509060046155956213154f83015460ff1690565b61559e81613841565b036157015781549560e087901c60ff16936155b885613061565b60028514615611576001600160a01b0388169788919082156156f2576155e69060a01c63ffffffff16611d2f565b956155f081613061565b1561568d575b50835460019060e01c60ff1661560b81613061565b1461561e575b5050505050505050600090565b6156346129fc6129ee89600361563a9701613d32565b91615c2a565b61564657808080615611565b805460ff60e01b1916600160e11b17905560408051918252600260208301527f897d79304faec9279753c73e7c91028343e8cc2be0ef4e5759cceafb92c429b991a4600190565b615698908285615b42565b835460ff60e01b1916600160e01b1784558787877f897d79304faec9279753c73e7c91028343e8cc2be0ef4e5759cceafb92c429b9604051806156e98b826020600191939293604081019481520152565b0390a4386155f6565b50505050505050505050600090565b60405162461bcd60e51b815260206004820152601460248201527344726177206973206e6f7420636f6d706c65746560601b6044820152606490fd5b9061577d613d09926002548060d01c806157a2575b50506002805465ffffffffffff60a01b191660a09290921b65ffffffffffff60a01b16919091179055565b600280546001600160d01b031660d09290921b6001600160d01b031916919091179055565b4211156157d457600180546001600160d01b031660309290921b6001600160d01b0319169190911790555b3880615752565b507f2b1fa2edafe6f7b9e97c1a9e0c3660e645beb2dcaa2d45bdbf9beaf5472e1ec5600080a16157cd565b80600052600e602052604060002054801560001461582c57506000908152600d6020526040812054151591565b600192909150565b600254906001600160a01b038216610c0c576001600160a01b0381166001600160a01b031992909216821760025561586d906000615d13565b9081615877575090565b60008052600360205261508f907f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff615a60565b80156158e8575b6158bb8282615d13565b91826158c657505090565b60009182526003602052604090912061508f916001600160a01b031690615a60565b6002546001600160a01b038116610c0c576001600160a01b0319166001600160a01b038316176002556158b1565b6002546001600160a01b038281169261593a929091168314615977575b6000615d9c565b9081615944575090565b60008052600360205261508f907f3617319a054d772f909f7c479a2cebe5066e836a939412e32403c99029b92eff615e62565b600280546001600160a01b0319169055615933565b8015806159e5575b6159d0575b6159a38282615d9c565b91826159ae57505090565b60009182526003602052604090912061508f916001600160a01b031690615e62565b600280546001600160a01b0319169055615999565b506002546001600160a01b03838116911614615994565b6001600160a01b03166000818152600e6020526040902091909155612bf390600c615a60565b805460005b818110615a3657505060009055565b80615a4360019285614368565b90549060031b1c6000528184016020526000604081205501615a27565b6000828152600182016020526040902054615ab057805490600160401b821015613c775782615a9961514e846001809601855584614368565b905580549260005201602052604060002055600190565b5050600090565b65ffffffffffff8111615acf5765ffffffffffff1690565b6306dfcc6560e41b600052603060045260245260446000fd5b604051949360209360009390615afe8689613c98565b8488526001600160401b038511613c775784936020890196601f190136883760208451940192f1903d9081615b39575b6000908286523e9190565b60009150615b2e565b6001600160a01b0382166000818152600e602052604081205492949390831580615c16575b615c025750615b78855480946140b8565b9081615bf2575050615b8990615cf8565b505b615b9442600b55565b615bb0615ba9612efa61027686015484613e04565b80926140b8565b9080615bdd575b5080615bc257505050565b6102759290920154613d0992906001600160a01b0316615369565b612f1b615bec91600a54613c12565b38615bb7565b615bfc92506159fc565b50615b8b565b63015ab34360e11b81526004839052602490fd5b50828152600d602052604081205415615b67565b600854600093926001600160a01b0392831692909116823b15615ca9576040516323b872dd60e01b81526001600160a01b03918216600482015291166024820152604481019290925290919081908390606490829084905af19182615c99575b5090615c935790565b50600190565b81615ca391613c98565b38615c8a565b8480fd5b906040519182815491828252602082019060005260206000209260005b818110615cdf575050613d0992500383613c98565b8454835260019485019487945060209093019201615cca565b612bf39080600052600e60205260006040812055600c615e62565b6000818152602081815260408083206001600160a01b038616845290915290205460ff16615ab0576000818152602081815260408083206001600160a01b03861684529091529020805460ff1916600117905533916001600160a01b0316907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d600080a4600190565b6000818152602081815260408083206001600160a01b038616845290915290205460ff1615615ab0576000818152602081815260408083206001600160a01b03861684529091529020805460ff1916905533916001600160a01b0316907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b600080a4600190565b80548015615e4c576000190190615e3a8282614368565b8154906000199060031b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b6001810191806000528260205260406000205492831515600014615efb576000198401848111610615578354600019810194908511610615576000958583615eb69761291b9503615ebc575b505050615e23565b55600190565b615ee2615edc91615ed36128e5615ef29588614368565b92839187614368565b9061510e565b8590600052602052604060002090565b55388080615eae565b5050505060009056fed453961ef428feffe24929a70412f0b1814ec58da7efbafe9d7cdc4bb965d3d6df273cb619d95419a9cd0ec88123a0538c85064229baa6363788f743fff90deba264697066735822122098430817c08be1108a50fbd5c9eb4f720f9ac9118b579a382d69d613144a080b64736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000002386f26fc100000000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a420000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a4200000000000000000000000000000000000000000000000000000000000000fa000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f13400000000000000000000000000000000000000000000000000000000000000780000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a4200000000000000000000000000000000000000000000000000000000000000010000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a4200000000000000000000000000000000000000000000000000000000000000010000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42
-----Decoded View---------------
Arg [0] : initialDelay_ (uint48): 0
Arg [1] : initialDefaultAdmin_ (address): 0x5AF3563b309CE018e784FB27C0eD7EcE9cA33A42
Arg [2] : platformAdmins_ (address[]): 0x5AF3563b309CE018e784FB27C0eD7EcE9cA33A42
Arg [3] : raffleAdmins_ (address[]): 0x5AF3563b309CE018e784FB27C0eD7EcE9cA33A42
Arg [4] : batchOperators_ (address[]): 0x5AF3563b309CE018e784FB27C0eD7EcE9cA33A42
Arg [5] : minimumBalance_ (uint256): 10000000000000000
Arg [6] : nftTreasury_ (address): 0x5AF3563b309CE018e784FB27C0eD7EcE9cA33A42
Arg [7] : platformFeeTreasury_ (address): 0x5AF3563b309CE018e784FB27C0eD7EcE9cA33A42
Arg [8] : platformFeePercentage_ (uint256): 250
Arg [9] : entropyAddress_ (address): 0xD458261E832415CFd3BAE5E416FdF3230ce6F134
Arg [10] : minOpenPeriodSeconds_ (uint256): 120
Arg [11] : wrappedNativeToken_ (address): 0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A
-----Encoded View---------------
18 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [1] : 0000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [3] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000200
Arg [5] : 000000000000000000000000000000000000000000000000002386f26fc10000
Arg [6] : 0000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42
Arg [7] : 0000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42
Arg [8] : 00000000000000000000000000000000000000000000000000000000000000fa
Arg [9] : 000000000000000000000000d458261e832415cfd3bae5e416fdf3230ce6f134
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000078
Arg [11] : 0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [13] : 0000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [15] : 0000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [17] : 0000000000000000000000005af3563b309ce018e784fb27c0ed7ece9ca33a42
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.03
Net Worth in MON
Token Allocations
MON
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| MONAD | 100.00% | $0.01891 | 1.555 | $0.029405 |
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.