编译器
0.8.20+commit.a1b79de6
文件 1 的 24:Arrays.sol
pragma solidity 0.8.20;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
library Arrays {
function findUpperBound(uint256[] memory array, uint256 element) internal pure returns (uint256) {
if (array.length == 0) {
return 0;
}
uint256 low = 0;
uint256 high = array.length;
while (low < high) {
uint256 mid = Math.average(low, high);
if (array[mid] > element) {
high = mid;
} else {
unchecked {
low = mid + 1;
}
}
}
if (low > 0 && array[low - 1] == element) {
unchecked {
return low - 1;
}
} else {
return low;
}
}
}
文件 2 的 24:GenericErrors.sol
pragma solidity ^0.8.17;
error NotAContract();
文件 3 的 24:IERC1155.sol
pragma solidity ^0.8.17;
interface IERC1155 {
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
文件 4 的 24:IERC20.sol
pragma solidity ^0.8.17;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 5 的 24:IERC721.sol
pragma solidity ^0.8.17;
interface IERC721 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 6 的 24:IOwnableTwoSteps.sol
pragma solidity ^0.8.17;
interface IOwnableTwoSteps {
enum Status {
NoOngoingTransfer,
TransferInProgress,
RenouncementInProgress
}
error NoOngoingTransferInProgress();
error NotOwner();
error RenouncementNotInProgress();
error TransferAlreadyInProgress();
error TransferNotInProgress();
error WrongPotentialOwner();
event CancelOwnershipTransfer();
event InitiateOwnershipRenouncement();
event InitiateOwnershipTransfer(address previousOwner, address potentialOwner);
event NewOwner(address newOwner);
}
文件 7 的 24:IRaffleV2.sol
pragma solidity 0.8.20;
interface IRaffleV2 {
enum RaffleStatus {
None,
Open,
Drawing,
RandomnessFulfilled,
Drawn,
Complete,
Refundable,
Cancelled
}
enum TokenType {
ERC721,
ERC1155,
ETH,
ERC20
}
struct PricingOption {
uint40 entriesCount;
uint208 price;
}
struct Entry {
uint40 currentEntryIndex;
address participant;
}
struct Winner {
address participant;
bool claimed;
uint8 prizeIndex;
uint40 entryIndex;
}
struct Prize {
uint40 winnersCount;
uint40 cumulativeWinnersCount;
TokenType prizeType;
uint8 prizeTier;
address prizeAddress;
uint256 prizeId;
uint256 prizeAmount;
}
struct Raffle {
address owner;
RaffleStatus status;
bool isMinimumEntriesFixed;
uint40 cutoffTime;
uint40 drawnAt;
uint40 minimumEntries;
uint40 maximumEntriesPerParticipant;
address feeTokenAddress;
uint16 protocolFeeBp;
uint208 claimableFees;
PricingOption[] pricingOptions;
Prize[] prizes;
Entry[] entries;
Winner[] winners;
}
struct ParticipantStats {
uint208 amountPaid;
uint40 entriesCount;
bool refunded;
}
struct EntryCalldata {
uint256 raffleId;
uint256 pricingOptionIndex;
uint40 count;
address recipient;
}
struct CreateRaffleCalldata {
uint40 cutoffTime;
bool isMinimumEntriesFixed;
uint40 minimumEntries;
uint40 maximumEntriesPerParticipant;
uint16 protocolFeeBp;
address feeTokenAddress;
Prize[] prizes;
PricingOption[] pricingOptions;
}
struct ClaimPrizesCalldata {
uint256 raffleId;
uint256[] winnerIndices;
}
struct RandomnessRequest {
bool exists;
uint80 raffleId;
uint256 randomWord;
}
struct TransferAccumulator {
address tokenAddress;
uint256 amount;
}
event CurrenciesStatusUpdated(address[] currencies, bool isAllowed);
event EntryRefunded(uint256 raffleId, address buyer, uint208 amount);
event EntrySold(uint256 raffleId, address buyer, address recipient, uint40 entriesCount, uint208 price);
event FeesClaimed(uint256 raffleId, uint256 amount);
event PrizeClaimed(uint256 raffleId, uint256 winnerIndex);
event PrizesClaimed(uint256 raffleId, uint256[] winnerIndex);
event ProtocolFeeBpUpdated(uint16 protocolFeeBp);
event ProtocolFeeRecipientUpdated(address protocolFeeRecipient);
event RaffleStatusUpdated(uint256 raffleId, RaffleStatus status);
event RandomnessRequested(uint256 raffleId, uint256 requestId);
error CutoffTimeNotReached();
error CutoffTimeReached();
error DrawExpirationTimeNotReached();
error InsufficientNativeTokensSupplied();
error InvalidCaller();
error InvalidCount();
error InvalidCurrency();
error InvalidCutoffTime();
error InvalidIndex();
error InvalidPricingOption();
error InvalidPricingOptionsCount();
error InvalidPrize();
error InvalidPrizesCount();
error InvalidProtocolFeeBp();
error InvalidProtocolFeeRecipient();
error InvalidStatus();
error InvalidWinnersCount();
error MaximumEntriesPerParticipantReached();
error MaximumEntriesReached();
error NothingToClaim();
error NotEnoughEntries();
error RandomnessRequestAlreadyExists();
error RandomnessRequestDoesNotExist();
function createRaffle(CreateRaffleCalldata calldata params) external payable returns (uint256 raffleId);
function enterRaffles(EntryCalldata[] calldata entries) external payable;
function selectWinners(uint256 requestId) external;
function claimPrize(uint256 raffleId, uint256 winnerIndex) external;
function claimPrizes(ClaimPrizesCalldata[] calldata claimPrizesCalldata) external;
function claimFees(uint256 raffleId) external;
function cancel(uint256 raffleId) external;
function drawWinners(uint256 raffleId) external;
function cancelAfterRandomnessRequest(uint256 raffleId) external;
function withdrawPrizes(uint256 raffleId) external;
function rollover(uint256[] calldata refundableRaffleIds, EntryCalldata[] calldata entries) external payable;
function claimRefund(uint256[] calldata raffleIds) external;
function setProtocolFeeBp(uint16 protocolFeeBp) external;
function setProtocolFeeRecipient(address protocolFeeRecipient) external;
function updateCurrenciesStatus(address[] calldata currencies, bool isAllowed) external;
function togglePaused() external;
function getWinners(uint256 raffleId) external view returns (Winner[] memory);
function getPricingOptions(uint256 raffleId) external view returns (PricingOption[] memory);
function getPrizes(uint256 raffleId) external view returns (Prize[] memory);
function getEntries(uint256 raffleId) external view returns (Entry[] memory);
}
文件 8 的 24:IReentrancyGuard.sol
pragma solidity ^0.8.17;
interface IReentrancyGuard {
error ReentrancyFail();
}
文件 9 的 24:ITransferManager.sol
pragma solidity 0.8.20;
import {TokenType} from "../enums/TokenType.sol";
interface ITransferManager {
struct BatchTransferItem {
address tokenAddress;
TokenType tokenType;
uint256[] itemIds;
uint256[] amounts;
}
event ApprovalsGranted(address user, address[] operators);
event ApprovalsRemoved(address user, address[] operators);
event OperatorAllowed(address operator);
event OperatorRemoved(address operator);
error OperatorAlreadyApprovedByUser();
error OperatorNotApprovedByUser();
error OperatorAlreadyAllowed();
error OperatorNotAllowed();
error TransferCallerInvalid();
function transferERC20(
address tokenAddress,
address from,
address to,
uint256 amount
) external;
function transferItemERC721(
address tokenAddress,
address from,
address to,
uint256 itemId
) external;
function transferItemsERC721(
address tokenAddress,
address from,
address to,
uint256[] calldata itemIds,
uint256[] calldata amounts
) external;
function transferItemERC1155(
address tokenAddress,
address from,
address to,
uint256 itemId,
uint256 amount
) external;
function transferItemsERC1155(
address tokenAddress,
address from,
address to,
uint256[] calldata itemIds,
uint256[] calldata amounts
) external;
function transferBatchItemsAcrossCollections(
BatchTransferItem[] calldata items,
address from,
address to
) external;
function grantApprovals(address[] calldata operators) external;
function revokeApprovals(address[] calldata operators) external;
function allowOperator(address operator) external;
function removeOperator(address operator) external;
}
文件 10 的 24:IWETH.sol
pragma solidity >=0.5.0;
interface IWETH {
function deposit() external payable;
function transfer(address dst, uint256 wad) external returns (bool);
function withdraw(uint256 wad) external;
}
文件 11 的 24:LowLevelERC1155Transfer.sol
pragma solidity ^0.8.17;
import {IERC1155} from "../interfaces/generic/IERC1155.sol";
import {ERC1155SafeTransferFromFail, ERC1155SafeBatchTransferFromFail} from "../errors/LowLevelErrors.sol";
import {NotAContract} from "../errors/GenericErrors.sol";
contract LowLevelERC1155Transfer {
function _executeERC1155SafeTransferFrom(
address collection,
address from,
address to,
uint256 tokenId,
uint256 amount
) internal {
if (collection.code.length == 0) {
revert NotAContract();
}
(bool status, ) = collection.call(abi.encodeCall(IERC1155.safeTransferFrom, (from, to, tokenId, amount, "")));
if (!status) {
revert ERC1155SafeTransferFromFail();
}
}
function _executeERC1155SafeBatchTransferFrom(
address collection,
address from,
address to,
uint256[] calldata tokenIds,
uint256[] calldata amounts
) internal {
if (collection.code.length == 0) {
revert NotAContract();
}
(bool status, ) = collection.call(
abi.encodeCall(IERC1155.safeBatchTransferFrom, (from, to, tokenIds, amounts, ""))
);
if (!status) {
revert ERC1155SafeBatchTransferFromFail();
}
}
}
文件 12 的 24:LowLevelERC20Transfer.sol
pragma solidity ^0.8.17;
import {IERC20} from "../interfaces/generic/IERC20.sol";
import {ERC20TransferFail, ERC20TransferFromFail} from "../errors/LowLevelErrors.sol";
import {NotAContract} from "../errors/GenericErrors.sol";
contract LowLevelERC20Transfer {
function _executeERC20TransferFrom(address currency, address from, address to, uint256 amount) internal {
if (currency.code.length == 0) {
revert NotAContract();
}
(bool status, bytes memory data) = currency.call(abi.encodeCall(IERC20.transferFrom, (from, to, amount)));
if (!status) {
revert ERC20TransferFromFail();
}
if (data.length > 0) {
if (!abi.decode(data, (bool))) {
revert ERC20TransferFromFail();
}
}
}
function _executeERC20DirectTransfer(address currency, address to, uint256 amount) internal {
if (currency.code.length == 0) {
revert NotAContract();
}
(bool status, bytes memory data) = currency.call(abi.encodeCall(IERC20.transfer, (to, amount)));
if (!status) {
revert ERC20TransferFail();
}
if (data.length > 0) {
if (!abi.decode(data, (bool))) {
revert ERC20TransferFail();
}
}
}
}
文件 13 的 24:LowLevelERC721Transfer.sol
pragma solidity ^0.8.17;
import {IERC721} from "../interfaces/generic/IERC721.sol";
import {ERC721TransferFromFail} from "../errors/LowLevelErrors.sol";
import {NotAContract} from "../errors/GenericErrors.sol";
contract LowLevelERC721Transfer {
function _executeERC721TransferFrom(address collection, address from, address to, uint256 tokenId) internal {
if (collection.code.length == 0) {
revert NotAContract();
}
(bool status, ) = collection.call(abi.encodeCall(IERC721.transferFrom, (from, to, tokenId)));
if (!status) {
revert ERC721TransferFromFail();
}
}
}
文件 14 的 24:LowLevelErrors.sol
pragma solidity ^0.8.17;
error ETHTransferFail();
error ERC20ApprovalFail();
error ERC20TransferFail();
error ERC20TransferFromFail();
error ERC721TransferFromFail();
error ERC1155SafeTransferFromFail();
error ERC1155SafeBatchTransferFromFail();
文件 15 的 24:LowLevelWETH.sol
pragma solidity ^0.8.17;
import {IWETH} from "../interfaces/generic/IWETH.sol";
contract LowLevelWETH {
function _transferETHAndWrapIfFailWithGasLimit(
address _WETH,
address _to,
uint256 _amount,
uint256 _gasLimit
) internal {
bool status;
assembly {
status := call(_gasLimit, _to, _amount, 0, 0, 0, 0)
}
if (!status) {
IWETH(_WETH).deposit{value: _amount}();
IWETH(_WETH).transfer(_to, _amount);
}
}
}
文件 16 的 24:Math.sol
pragma solidity ^0.8.0;
library Math {
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b + (a % b == 0 ? 0 : 1);
}
}
文件 17 的 24:OwnableTwoSteps.sol
pragma solidity ^0.8.17;
import {IOwnableTwoSteps} from "./interfaces/IOwnableTwoSteps.sol";
abstract contract OwnableTwoSteps is IOwnableTwoSteps {
address public owner;
address public potentialOwner;
Status public ownershipStatus;
modifier onlyOwner() {
_onlyOwner();
_;
}
constructor(address _owner) {
owner = _owner;
emit NewOwner(_owner);
}
function cancelOwnershipTransfer() external onlyOwner {
Status _ownershipStatus = ownershipStatus;
if (_ownershipStatus == Status.NoOngoingTransfer) {
revert NoOngoingTransferInProgress();
}
if (_ownershipStatus == Status.TransferInProgress) {
delete potentialOwner;
}
delete ownershipStatus;
emit CancelOwnershipTransfer();
}
function confirmOwnershipRenouncement() external onlyOwner {
if (ownershipStatus != Status.RenouncementInProgress) {
revert RenouncementNotInProgress();
}
delete owner;
delete ownershipStatus;
emit NewOwner(address(0));
}
function confirmOwnershipTransfer() external {
if (ownershipStatus != Status.TransferInProgress) {
revert TransferNotInProgress();
}
if (msg.sender != potentialOwner) {
revert WrongPotentialOwner();
}
owner = msg.sender;
delete ownershipStatus;
delete potentialOwner;
emit NewOwner(msg.sender);
}
function initiateOwnershipTransfer(address newPotentialOwner) external onlyOwner {
if (ownershipStatus != Status.NoOngoingTransfer) {
revert TransferAlreadyInProgress();
}
ownershipStatus = Status.TransferInProgress;
potentialOwner = newPotentialOwner;
emit InitiateOwnershipTransfer(msg.sender, newPotentialOwner);
}
function initiateOwnershipRenouncement() external onlyOwner {
if (ownershipStatus != Status.NoOngoingTransfer) {
revert TransferAlreadyInProgress();
}
ownershipStatus = Status.RenouncementInProgress;
emit InitiateOwnershipRenouncement();
}
function _onlyOwner() private view {
if (msg.sender != owner) revert NotOwner();
}
}
文件 18 的 24:PackableReentrancyGuard.sol
pragma solidity ^0.8.17;
import {IReentrancyGuard} from "./interfaces/IReentrancyGuard.sol";
abstract contract PackableReentrancyGuard is IReentrancyGuard {
uint8 private _status;
modifier nonReentrant() {
if (_status == 2) {
revert ReentrancyFail();
}
_status = 2;
_;
_status = 1;
}
constructor() {
_status = 1;
}
}
文件 19 的 24:Pausable.sol
pragma solidity ^0.8.17;
abstract contract Pausable {
event Paused(address account);
event Unpaused(address account);
error IsPaused();
error NotPaused();
bool private _paused;
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
if (paused()) {
revert IsPaused();
}
}
function _requirePaused() internal view virtual {
if (!paused()) {
revert NotPaused();
}
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(msg.sender);
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(msg.sender);
}
}
文件 20 的 24:RaffleV2.sol
pragma solidity 0.8.20;
import {LowLevelWETH} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelWETH.sol";
import {LowLevelERC20Transfer} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelERC20Transfer.sol";
import {LowLevelERC721Transfer} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelERC721Transfer.sol";
import {LowLevelERC1155Transfer} from "@looksrare/contracts-libs/contracts/lowLevelCallers/LowLevelERC1155Transfer.sol";
import {OwnableTwoSteps} from "@looksrare/contracts-libs/contracts/OwnableTwoSteps.sol";
import {PackableReentrancyGuard} from "@looksrare/contracts-libs/contracts/PackableReentrancyGuard.sol";
import {Pausable} from "@looksrare/contracts-libs/contracts/Pausable.sol";
import {ITransferManager} from "@looksrare/contracts-transfer-manager/contracts/interfaces/ITransferManager.sol";
import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import {Arrays} from "./libraries/Arrays.sol";
import {WinningEntrySearchLogicV2} from "./WinningEntrySearchLogicV2.sol";
import {IRaffleV2} from "./interfaces/IRaffleV2.sol";
contract RaffleV2 is
IRaffleV2,
LowLevelWETH,
LowLevelERC20Transfer,
LowLevelERC721Transfer,
LowLevelERC1155Transfer,
VRFConsumerBaseV2,
OwnableTwoSteps,
PackableReentrancyGuard,
Pausable,
WinningEntrySearchLogicV2
{
using Arrays for uint256[];
address private immutable WETH;
uint256 private constant ONE_DAY = 86_400 seconds;
uint256 private constant ONE_WEEK = 604_800 seconds;
uint256 private constant ONE_HUNDRED_PERCENT_BP = 10_000;
mapping(uint256 => Raffle) public raffles;
mapping(uint256 => mapping(address => ParticipantStats)) public rafflesParticipantsStats;
mapping(address => uint256) public isCurrencyAllowed;
uint256 public constant MAXIMUM_NUMBER_OF_PRIZES_PER_RAFFLE = 200;
uint40 public constant MAXIMUM_NUMBER_OF_WINNERS_PER_RAFFLE = 200;
bytes32 private immutable KEY_HASH;
uint64 public immutable SUBSCRIPTION_ID;
VRFCoordinatorV2Interface private immutable VRF_COORDINATOR;
mapping(uint256 => RandomnessRequest) public randomnessRequests;
uint16 public constant MAXIMUM_PROTOCOL_FEE_BP = 2_500;
uint80 public rafflesCount;
address public protocolFeeRecipient;
uint16 public protocolFeeBp;
uint256 public constant MAXIMUM_PRICING_OPTIONS_PER_RAFFLE = 5;
ITransferManager private immutable transferManager;
constructor(
address _weth,
bytes32 _keyHash,
uint64 _subscriptionId,
address _vrfCoordinator,
address _owner,
address _protocolFeeRecipient,
uint16 _protocolFeeBp,
address _transferManager
) VRFConsumerBaseV2(_vrfCoordinator) OwnableTwoSteps(_owner) {
_setProtocolFeeBp(_protocolFeeBp);
_setProtocolFeeRecipient(_protocolFeeRecipient);
WETH = _weth;
KEY_HASH = _keyHash;
VRF_COORDINATOR = VRFCoordinatorV2Interface(_vrfCoordinator);
SUBSCRIPTION_ID = _subscriptionId;
transferManager = ITransferManager(_transferManager);
}
function createRaffle(CreateRaffleCalldata calldata params)
external
payable
nonReentrant
whenNotPaused
returns (uint256 raffleId)
{
uint40 cutoffTime = params.cutoffTime;
if (_unsafeAdd(block.timestamp, ONE_DAY) > cutoffTime || cutoffTime > _unsafeAdd(block.timestamp, ONE_WEEK)) {
revert InvalidCutoffTime();
}
uint16 agreedProtocolFeeBp = params.protocolFeeBp;
if (agreedProtocolFeeBp != protocolFeeBp) {
revert InvalidProtocolFeeBp();
}
address feeTokenAddress = params.feeTokenAddress;
if (feeTokenAddress != address(0)) {
_validateCurrency(feeTokenAddress);
}
uint256 prizesCount = params.prizes.length;
if (prizesCount == 0 || prizesCount > MAXIMUM_NUMBER_OF_PRIZES_PER_RAFFLE) {
revert InvalidPrizesCount();
}
unchecked {
raffleId = ++rafflesCount;
}
uint256 raffleSlot;
uint256 prizesLengthSlot;
uint256 individualPrizeSlotOffset;
assembly {
mstore(0x00, raffleId)
mstore(0x20, raffles.slot)
raffleSlot := keccak256(0x00, 0x40)
prizesLengthSlot := add(keccak256(0x00, 0x40), 4)
mstore(0x00, prizesLengthSlot)
individualPrizeSlotOffset := keccak256(0x00, 0x20)
}
uint256 expectedEthValue;
uint40 cumulativeWinnersCount;
{
uint8 currentPrizeTier;
for (uint256 i; i < prizesCount; ) {
Prize memory prize = params.prizes[i];
uint8 prizeTier = prize.prizeTier;
if (prizeTier < currentPrizeTier) {
revert InvalidPrize();
}
_validatePrize(prize);
TokenType prizeType = prize.prizeType;
uint40 winnersCount = prize.winnersCount;
address prizeAddress = prize.prizeAddress;
uint256 prizeId = prize.prizeId;
uint256 prizeAmount = prize.prizeAmount;
if (prizeType == TokenType.ERC721) {
transferManager.transferItemERC721(prizeAddress, msg.sender, address(this), prizeId);
} else if (prizeType == TokenType.ERC20) {
transferManager.transferERC20(prizeAddress, msg.sender, address(this), prizeAmount * winnersCount);
} else if (prizeType == TokenType.ETH) {
expectedEthValue += (prizeAmount * winnersCount);
} else {
transferManager.transferItemERC1155(
prizeAddress,
msg.sender,
address(this),
prizeId,
prizeAmount * winnersCount
);
}
cumulativeWinnersCount += winnersCount;
currentPrizeTier = prizeTier;
assembly {
let prizeSlotOne := winnersCount
prizeSlotOne := or(prizeSlotOne, shl(40, cumulativeWinnersCount))
prizeSlotOne := or(prizeSlotOne, shl(80, prizeType))
prizeSlotOne := or(prizeSlotOne, shl(88, prizeTier))
prizeSlotOne := or(prizeSlotOne, shl(96, prizeAddress))
let currentPrizeSlotOffset := add(individualPrizeSlotOffset, mul(i, 3))
sstore(currentPrizeSlotOffset, prizeSlotOne)
sstore(add(currentPrizeSlotOffset, 1), prizeId)
sstore(add(currentPrizeSlotOffset, 2), prizeAmount)
}
unchecked {
++i;
}
}
assembly {
sstore(prizesLengthSlot, prizesCount)
}
}
_validateExpectedEthValueOrRefund(expectedEthValue);
uint40 minimumEntries = params.minimumEntries;
if (cumulativeWinnersCount > minimumEntries || cumulativeWinnersCount > MAXIMUM_NUMBER_OF_WINNERS_PER_RAFFLE) {
revert InvalidWinnersCount();
}
_validateAndSetPricingOptions(raffleId, minimumEntries, params.pricingOptions);
bool isMinimumEntriesFixed = params.isMinimumEntriesFixed;
uint40 maximumEntriesPerParticipant = params.maximumEntriesPerParticipant;
assembly {
let raffleSlotOneValue := caller()
raffleSlotOneValue := or(raffleSlotOneValue, shl(160, 1))
raffleSlotOneValue := or(raffleSlotOneValue, shl(168, isMinimumEntriesFixed))
raffleSlotOneValue := or(raffleSlotOneValue, shl(176, cutoffTime))
let raffleSlotTwoValue := minimumEntries
raffleSlotTwoValue := or(raffleSlotTwoValue, shl(40, maximumEntriesPerParticipant))
raffleSlotTwoValue := or(raffleSlotTwoValue, shl(80, feeTokenAddress))
raffleSlotTwoValue := or(raffleSlotTwoValue, shl(240, agreedProtocolFeeBp))
sstore(raffleSlot, raffleSlotOneValue)
sstore(add(raffleSlot, 1), raffleSlotTwoValue)
}
emit RaffleStatusUpdated(raffleId, RaffleStatus.Open);
}
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external pure returns (bytes4) {
return this.onERC1155Received.selector;
}
function enterRaffles(EntryCalldata[] calldata entries) external payable nonReentrant whenNotPaused {
(address feeTokenAddress, uint208 expectedValue) = _enterRaffles(entries);
_chargeUser(feeTokenAddress, expectedValue);
}
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override {
if (randomnessRequests[_requestId].exists) {
uint256 raffleId = randomnessRequests[_requestId].raffleId;
Raffle storage raffle = raffles[raffleId];
if (raffle.status == RaffleStatus.Drawing) {
_setRaffleStatus(raffle, raffleId, RaffleStatus.RandomnessFulfilled);
randomnessRequests[_requestId].randomWord = _randomWords[0];
}
}
}
function selectWinners(uint256 requestId) external {
RandomnessRequest memory randomnessRequest = randomnessRequests[requestId];
if (!randomnessRequest.exists) {
revert RandomnessRequestDoesNotExist();
}
uint256 raffleId = randomnessRequest.raffleId;
Raffle storage raffle = raffles[raffleId];
_validateRaffleStatus(raffle, RaffleStatus.RandomnessFulfilled);
_setRaffleStatus(raffle, raffleId, RaffleStatus.Drawn);
Prize[] storage prizes = raffle.prizes;
uint256 prizesCount = prizes.length;
uint256 winnersCount = prizes[prizesCount - 1].cumulativeWinnersCount;
Entry[] memory entries = raffle.entries;
uint256 entriesCount = entries.length;
uint256[] memory currentEntryIndexArray = new uint256[](entriesCount);
for (uint256 i; i < entriesCount; ) {
currentEntryIndexArray[i] = entries[i].currentEntryIndex;
unchecked {
++i;
}
}
uint256 currentEntryIndex = uint256(currentEntryIndexArray[entriesCount - 1]);
uint256[] memory winningEntriesBitmap = new uint256[]((currentEntryIndex >> 8) + 1);
uint256[] memory cumulativeWinnersCountArray = new uint256[](prizesCount);
for (uint256 i; i < prizesCount; ) {
cumulativeWinnersCountArray[i] = prizes[i].cumulativeWinnersCount;
unchecked {
++i;
}
}
uint256 randomWord = randomnessRequest.randomWord;
uint256 winningEntry;
uint256 winnersLengthSlot;
uint256 individualWinnerSlotOffset;
assembly {
mstore(0x00, raffleId)
mstore(0x20, raffles.slot)
winnersLengthSlot := add(keccak256(0x00, 0x40), 6)
mstore(0x00, winnersLengthSlot)
individualWinnerSlotOffset := keccak256(0x00, 0x20)
}
for (uint256 i; i < winnersCount; ) {
(randomWord, winningEntry, winningEntriesBitmap) = _searchForWinningEntryUntilThereIsNotADuplicate(
randomWord,
currentEntryIndex,
winningEntriesBitmap
);
address participant = entries[currentEntryIndexArray.findUpperBound(winningEntry)].participant;
uint256 prizeIndex = cumulativeWinnersCountArray.findUpperBound(_unsafeAdd(i, 1));
assembly {
let winnerSlotValue := participant
winnerSlotValue := or(winnerSlotValue, shl(168, prizeIndex))
winnerSlotValue := or(winnerSlotValue, shl(176, winningEntry))
sstore(add(individualWinnerSlotOffset, i), winnerSlotValue)
}
randomWord = uint256(keccak256(abi.encodePacked(randomWord)));
unchecked {
++i;
}
}
assembly {
sstore(winnersLengthSlot, winnersCount)
}
}
function claimPrize(uint256 raffleId, uint256 winnerIndex) external nonReentrant whenNotPaused {
Raffle storage raffle = raffles[raffleId];
if (raffle.status != RaffleStatus.Drawn) {
_validateRaffleStatus(raffle, RaffleStatus.Complete);
}
Winner[] storage winners = raffle.winners;
if (winnerIndex >= winners.length) {
revert InvalidIndex();
}
Winner storage winner = winners[winnerIndex];
if (winner.claimed) {
revert NothingToClaim();
}
_validateCaller(winner.participant);
winner.claimed = true;
_transferPrize({prize: raffle.prizes[winner.prizeIndex], recipient: msg.sender, multiplier: 1});
emit PrizeClaimed(raffleId, winnerIndex);
}
function claimPrizes(ClaimPrizesCalldata[] calldata claimPrizesCalldata) external nonReentrant whenNotPaused {
TransferAccumulator memory transferAccumulator;
for (uint256 i; i < claimPrizesCalldata.length; ) {
ClaimPrizesCalldata calldata perRaffleClaimPrizesCalldata = claimPrizesCalldata[i];
uint256 raffleId = perRaffleClaimPrizesCalldata.raffleId;
Raffle storage raffle = raffles[raffleId];
if (raffle.status != RaffleStatus.Drawn) {
_validateRaffleStatus(raffle, RaffleStatus.Complete);
}
Winner[] storage winners = raffle.winners;
uint256[] calldata winnerIndices = perRaffleClaimPrizesCalldata.winnerIndices;
uint256 winnersCount = winners.length;
uint256 claimsCount = winnerIndices.length;
for (uint256 j; j < claimsCount; ) {
uint256 winnerIndex = winnerIndices[j];
if (winnerIndex >= winnersCount) {
revert InvalidIndex();
}
Winner storage winner = winners[winnerIndex];
if (winner.claimed) {
revert NothingToClaim();
}
_validateCaller(winner.participant);
winner.claimed = true;
Prize storage prize = raffle.prizes[winner.prizeIndex];
if (prize.prizeType > TokenType.ERC1155) {
address prizeAddress = prize.prizeAddress;
if (prizeAddress == transferAccumulator.tokenAddress) {
transferAccumulator.amount += prize.prizeAmount;
} else {
if (transferAccumulator.amount != 0) {
_transferFungibleTokens(transferAccumulator);
}
transferAccumulator.tokenAddress = prizeAddress;
transferAccumulator.amount = prize.prizeAmount;
}
} else {
_transferPrize({prize: prize, recipient: msg.sender, multiplier: 1});
}
unchecked {
++j;
}
}
emit PrizesClaimed(raffleId, winnerIndices);
unchecked {
++i;
}
}
if (transferAccumulator.amount != 0) {
_transferFungibleTokens(transferAccumulator);
}
}
function claimFees(uint256 raffleId) external nonReentrant whenNotPaused {
Raffle storage raffle = raffles[raffleId];
_validateRaffleStatus(raffle, RaffleStatus.Drawn);
address raffleOwner = raffle.owner;
if (msg.sender != raffleOwner) {
_validateCaller(owner);
}
uint208 claimableFees = raffle.claimableFees;
uint208 protocolFees = (claimableFees * uint208(raffle.protocolFeeBp)) / uint208(ONE_HUNDRED_PERCENT_BP);
unchecked {
claimableFees -= protocolFees;
}
_setRaffleStatus(raffle, raffleId, RaffleStatus.Complete);
raffle.claimableFees = 0;
address feeTokenAddress = raffle.feeTokenAddress;
_transferFungibleTokens(feeTokenAddress, raffleOwner, claimableFees);
if (protocolFees != 0) {
_transferFungibleTokens(feeTokenAddress, protocolFeeRecipient, protocolFees);
}
emit FeesClaimed(raffleId, claimableFees);
}
function cancel(uint256 raffleId) external nonReentrant whenNotPaused {
Raffle storage raffle = raffles[raffleId];
_validateRafflePostCutoffTimeStatusTransferability(raffle);
if (block.timestamp < raffle.cutoffTime + 1 hours) {
_validateCaller(raffle.owner);
}
_setRaffleStatus(raffle, raffleId, RaffleStatus.Refundable);
}
function drawWinners(uint256 raffleId) external nonReentrant whenNotPaused {
Raffle storage raffle = raffles[raffleId];
Entry[] storage entries = raffle.entries;
uint256 entriesCount = entries.length;
if (entriesCount == 0) {
revert NotEnoughEntries();
}
Prize[] storage prizes = raffle.prizes;
if (prizes[prizes.length - 1].cumulativeWinnersCount > entries[entriesCount - 1].currentEntryIndex + 1) {
revert NotEnoughEntries();
}
_validateRafflePostCutoffTimeStatusTransferability(raffle);
_validateCaller(raffle.owner);
_drawWinners(raffleId, raffle);
}
function cancelAfterRandomnessRequest(uint256 raffleId) external nonReentrant whenNotPaused {
Raffle storage raffle = raffles[raffleId];
_validateRaffleStatus(raffle, RaffleStatus.Drawing);
if (block.timestamp < raffle.drawnAt + ONE_DAY) {
revert DrawExpirationTimeNotReached();
}
_setRaffleStatus(raffle, raffleId, RaffleStatus.Refundable);
}
function withdrawPrizes(uint256 raffleId) external nonReentrant whenNotPaused {
Raffle storage raffle = raffles[raffleId];
_validateRaffleStatus(raffle, RaffleStatus.Refundable);
_setRaffleStatus(raffle, raffleId, RaffleStatus.Cancelled);
uint256 prizesCount = raffle.prizes.length;
address raffleOwner = raffle.owner;
for (uint256 i; i < prizesCount; ) {
Prize storage prize = raffle.prizes[i];
_transferPrize({prize: prize, recipient: raffleOwner, multiplier: uint256(prize.winnersCount)});
unchecked {
++i;
}
}
}
function claimRefund(uint256[] calldata raffleIds) external nonReentrant whenNotPaused {
(address feeTokenAddress, uint208 refundAmount) = _claimRefund(raffleIds);
_transferFungibleTokens(feeTokenAddress, msg.sender, refundAmount);
}
function rollover(uint256[] calldata refundableRaffleIds, EntryCalldata[] calldata entries)
external
payable
nonReentrant
whenNotPaused
{
(address refundFeeTokenAddress, uint208 rolloverAmount) = _claimRefund(refundableRaffleIds);
(address enterRafflesFeeTokenAddress, uint208 expectedValue) = _enterRaffles(entries);
if (refundFeeTokenAddress != enterRafflesFeeTokenAddress) {
revert InvalidCurrency();
}
if (rolloverAmount > expectedValue) {
_transferFungibleTokens(refundFeeTokenAddress, msg.sender, _unsafeSubtract(rolloverAmount, expectedValue));
} else if (rolloverAmount < expectedValue) {
_chargeUser(refundFeeTokenAddress, _unsafeSubtract(expectedValue, rolloverAmount));
}
}
function setProtocolFeeRecipient(address _protocolFeeRecipient) external onlyOwner {
_setProtocolFeeRecipient(_protocolFeeRecipient);
}
function setProtocolFeeBp(uint16 _protocolFeeBp) external onlyOwner {
_setProtocolFeeBp(_protocolFeeBp);
}
function updateCurrenciesStatus(address[] calldata currencies, bool isAllowed) external onlyOwner {
uint256 count = currencies.length;
for (uint256 i; i < count; ) {
isCurrencyAllowed[currencies[i]] = (isAllowed ? 1 : 0);
unchecked {
++i;
}
}
emit CurrenciesStatusUpdated(currencies, isAllowed);
}
function togglePaused() external onlyOwner {
paused() ? _unpause() : _pause();
}
function getWinners(uint256 raffleId) external view returns (Winner[] memory winners) {
winners = raffles[raffleId].winners;
}
function getPrizes(uint256 raffleId) external view returns (Prize[] memory prizes) {
prizes = raffles[raffleId].prizes;
}
function getEntries(uint256 raffleId) external view returns (Entry[] memory entries) {
entries = raffles[raffleId].entries;
}
function getPricingOptions(uint256 raffleId) external view returns (PricingOption[] memory pricingOptions) {
pricingOptions = raffles[raffleId].pricingOptions;
}
function _setProtocolFeeRecipient(address _protocolFeeRecipient) private {
if (_protocolFeeRecipient == address(0)) {
revert InvalidProtocolFeeRecipient();
}
protocolFeeRecipient = _protocolFeeRecipient;
emit ProtocolFeeRecipientUpdated(_protocolFeeRecipient);
}
function _setProtocolFeeBp(uint16 _protocolFeeBp) private {
if (_protocolFeeBp > MAXIMUM_PROTOCOL_FEE_BP) {
revert InvalidProtocolFeeBp();
}
protocolFeeBp = _protocolFeeBp;
emit ProtocolFeeBpUpdated(_protocolFeeBp);
}
function _validateAndSetPricingOptions(
uint256 raffleId,
uint40 minimumEntries,
PricingOption[] calldata pricingOptions
) private {
uint256 count = pricingOptions.length;
if (count == 0 || count > MAXIMUM_PRICING_OPTIONS_PER_RAFFLE) {
revert InvalidPricingOptionsCount();
}
uint40 lowestEntriesCount = pricingOptions[0].entriesCount;
uint256 pricingOptionsLengthSlot;
uint256 individualPricingOptionSlotOffset;
assembly {
mstore(0x00, raffleId)
mstore(0x20, raffles.slot)
pricingOptionsLengthSlot := add(keccak256(0x00, 0x40), 3)
mstore(0x00, pricingOptionsLengthSlot)
individualPricingOptionSlotOffset := keccak256(0x00, 0x20)
}
for (uint256 i; i < count; ) {
PricingOption memory pricingOption = pricingOptions[i];
uint40 entriesCount = pricingOption.entriesCount;
uint208 price = pricingOption.price;
if (i == 0) {
if (minimumEntries % entriesCount != 0 || price == 0) {
revert InvalidPricingOption();
}
} else {
PricingOption memory lastPricingOption = pricingOptions[_unsafeSubtract(i, 1)];
uint208 lastPrice = lastPricingOption.price;
uint40 lastEntriesCount = lastPricingOption.entriesCount;
if (
entriesCount % lowestEntriesCount != 0 ||
price % entriesCount != 0 ||
entriesCount <= lastEntriesCount ||
price <= lastPrice ||
price / entriesCount > lastPrice / lastEntriesCount
) {
revert InvalidPricingOption();
}
}
assembly {
let pricingOptionValue := entriesCount
pricingOptionValue := or(pricingOptionValue, shl(40, price))
sstore(add(individualPricingOptionSlotOffset, i), pricingOptionValue)
}
unchecked {
++i;
}
}
assembly {
sstore(pricingOptionsLengthSlot, count)
}
}
function _validatePrize(Prize memory prize) private view {
TokenType prizeType = prize.prizeType;
if (prizeType == TokenType.ERC721) {
if (prize.prizeAmount != 1 || prize.winnersCount != 1) {
revert InvalidPrize();
}
} else {
if (prizeType == TokenType.ERC20) {
_validateCurrency(prize.prizeAddress);
}
if (prize.prizeAmount == 0 || prize.winnersCount == 0) {
revert InvalidPrize();
}
}
}
function _transferPrize(
Prize storage prize,
address recipient,
uint256 multiplier
) private {
TokenType prizeType = prize.prizeType;
address prizeAddress = prize.prizeAddress;
if (prizeType == TokenType.ERC721) {
_executeERC721TransferFrom(prizeAddress, address(this), recipient, prize.prizeId);
} else if (prizeType == TokenType.ERC1155) {
_executeERC1155SafeTransferFrom(
prizeAddress,
address(this),
recipient,
prize.prizeId,
prize.prizeAmount * multiplier
);
} else {
_transferFungibleTokens(prizeAddress, recipient, prize.prizeAmount * multiplier);
}
}
function _transferFungibleTokens(
address currency,
address recipient,
uint256 amount
) private {
if (currency == address(0)) {
_transferETHAndWrapIfFailWithGasLimit(WETH, recipient, amount, gasleft());
} else {
_executeERC20DirectTransfer(currency, recipient, amount);
}
}
function _transferFungibleTokens(TransferAccumulator memory transferAccumulator) private {
_transferFungibleTokens(transferAccumulator.tokenAddress, msg.sender, transferAccumulator.amount);
}
function _drawWinners(uint256 raffleId, Raffle storage raffle) private {
_setRaffleStatus(raffle, raffleId, RaffleStatus.Drawing);
raffle.drawnAt = uint40(block.timestamp);
uint256 requestId = VRF_COORDINATOR.requestRandomWords({
keyHash: KEY_HASH,
subId: SUBSCRIPTION_ID,
minimumRequestConfirmations: uint16(3),
callbackGasLimit: uint32(500_000),
numWords: uint32(1)
});
if (randomnessRequests[requestId].exists) {
revert RandomnessRequestAlreadyExists();
}
randomnessRequests[requestId].exists = true;
randomnessRequests[requestId].raffleId = uint80(raffleId);
emit RandomnessRequested(raffleId, requestId);
}
function _validateRaffleStatus(Raffle storage raffle, RaffleStatus status) private view {
if (raffle.status != status) {
revert InvalidStatus();
}
}
function _enterRaffles(EntryCalldata[] calldata entries)
private
returns (address feeTokenAddress, uint208 expectedValue)
{
uint256 count = entries.length;
for (uint256 i; i < count; ) {
EntryCalldata calldata entry = entries[i];
address recipient = entry.recipient == address(0) ? msg.sender : entry.recipient;
uint256 raffleId = entry.raffleId;
Raffle storage raffle = raffles[raffleId];
if (i == 0) {
feeTokenAddress = raffle.feeTokenAddress;
} else if (raffle.feeTokenAddress != feeTokenAddress) {
revert InvalidCurrency();
}
if (entry.pricingOptionIndex >= raffle.pricingOptions.length) {
revert InvalidIndex();
}
_validateRaffleStatus(raffle, RaffleStatus.Open);
if (block.timestamp >= raffle.cutoffTime) {
revert CutoffTimeReached();
}
uint40 entriesCount;
uint208 price;
{
PricingOption memory pricingOption = raffle.pricingOptions[entry.pricingOptionIndex];
uint40 multiplier = entry.count;
if (multiplier == 0) {
revert InvalidCount();
}
entriesCount = pricingOption.entriesCount * multiplier;
price = pricingOption.price * multiplier;
uint40 newParticipantEntriesCount = rafflesParticipantsStats[raffleId][recipient].entriesCount +
entriesCount;
if (newParticipantEntriesCount > raffle.maximumEntriesPerParticipant) {
revert MaximumEntriesPerParticipantReached();
}
rafflesParticipantsStats[raffleId][recipient].entriesCount = newParticipantEntriesCount;
}
expectedValue += price;
uint256 raffleEntriesCount = raffle.entries.length;
uint40 currentEntryIndex;
if (raffleEntriesCount == 0) {
currentEntryIndex = uint40(_unsafeSubtract(entriesCount, 1));
} else {
currentEntryIndex =
raffle.entries[_unsafeSubtract(raffleEntriesCount, 1)].currentEntryIndex +
entriesCount;
}
if (raffle.isMinimumEntriesFixed) {
if (currentEntryIndex >= raffle.minimumEntries) {
revert MaximumEntriesReached();
}
}
_pushEntry(raffle, currentEntryIndex, recipient);
raffle.claimableFees += price;
rafflesParticipantsStats[raffleId][msg.sender].amountPaid += price;
emit EntrySold(raffleId, msg.sender, recipient, entriesCount, price);
if (currentEntryIndex >= _unsafeSubtract(raffle.minimumEntries, 1)) {
_drawWinners(raffleId, raffle);
}
unchecked {
++i;
}
}
}
function _chargeUser(address feeTokenAddress, uint256 expectedValue) private {
if (feeTokenAddress == address(0)) {
_validateExpectedEthValueOrRefund(expectedValue);
} else {
transferManager.transferERC20(feeTokenAddress, msg.sender, address(this), expectedValue);
}
}
function _claimRefund(uint256[] calldata raffleIds)
private
returns (address feeTokenAddress, uint208 refundAmount)
{
uint256 count = raffleIds.length;
for (uint256 i; i < count; ) {
uint256 raffleId = raffleIds[i];
Raffle storage raffle = raffles[raffleId];
if (raffle.status < RaffleStatus.Refundable) {
revert InvalidStatus();
}
ParticipantStats storage stats = rafflesParticipantsStats[raffleId][msg.sender];
uint208 amountPaid = stats.amountPaid;
if (stats.refunded || amountPaid == 0) {
revert NothingToClaim();
}
if (i == 0) {
feeTokenAddress = raffle.feeTokenAddress;
} else if (feeTokenAddress != raffle.feeTokenAddress) {
revert InvalidCurrency();
}
stats.refunded = true;
refundAmount += amountPaid;
emit EntryRefunded(raffleId, msg.sender, amountPaid);
unchecked {
++i;
}
}
}
function _validateCaller(address caller) private view {
if (msg.sender != caller) {
revert InvalidCaller();
}
}
function _validateCurrency(address currency) private view {
if (isCurrencyAllowed[currency] != 1) {
revert InvalidCurrency();
}
}
function _validateExpectedEthValueOrRefund(uint256 expectedEthValue) private {
if (expectedEthValue > msg.value) {
revert InsufficientNativeTokensSupplied();
} else if (msg.value > expectedEthValue) {
_transferETHAndWrapIfFailWithGasLimit(
WETH,
msg.sender,
_unsafeSubtract(msg.value, expectedEthValue),
gasleft()
);
}
}
function _validateRafflePostCutoffTimeStatusTransferability(Raffle storage raffle) private view {
_validateRaffleStatus(raffle, RaffleStatus.Open);
if (raffle.cutoffTime > block.timestamp) {
revert CutoffTimeNotReached();
}
}
function _setRaffleStatus(
Raffle storage raffle,
uint256 raffleId,
RaffleStatus status
) private {
raffle.status = status;
emit RaffleStatusUpdated(raffleId, status);
}
function _pushEntry(
Raffle storage raffle,
uint40 currentEntryIndex,
address recipient
) private {
raffle.entries.push(Entry({currentEntryIndex: currentEntryIndex, participant: recipient}));
}
function _unsafeAdd(uint256 a, uint256 b) private pure returns (uint256) {
unchecked {
return a + b;
}
}
function _unsafeSubtract(uint256 a, uint256 b) private pure returns (uint256) {
unchecked {
return a - b;
}
}
}
文件 21 的 24:TokenType.sol
pragma solidity 0.8.20;
enum TokenType {
ERC20,
ERC721,
ERC1155
}
文件 22 的 24:VRFConsumerBaseV2.sol
pragma solidity ^0.8.4;
abstract contract VRFConsumerBaseV2 {
error OnlyCoordinatorCanFulfill(address have, address want);
address private immutable vrfCoordinator;
constructor(address _vrfCoordinator) {
vrfCoordinator = _vrfCoordinator;
}
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;
function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
if (msg.sender != vrfCoordinator) {
revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
}
fulfillRandomWords(requestId, randomWords);
}
}
文件 23 的 24:VRFCoordinatorV2Interface.sol
pragma solidity ^0.8.0;
interface VRFCoordinatorV2Interface {
function getRequestConfig()
external
view
returns (
uint16,
uint32,
bytes32[] memory
);
function requestRandomWords(
bytes32 keyHash,
uint64 subId,
uint16 minimumRequestConfirmations,
uint32 callbackGasLimit,
uint32 numWords
) external returns (uint256 requestId);
function createSubscription() external returns (uint64 subId);
function getSubscription(uint64 subId)
external
view
returns (
uint96 balance,
uint64 reqCount,
address owner,
address[] memory consumers
);
function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;
function acceptSubscriptionOwnerTransfer(uint64 subId) external;
function addConsumer(uint64 subId, address consumer) external;
function removeConsumer(uint64 subId, address consumer) external;
function cancelSubscription(uint64 subId, address to) external;
function pendingRequestExists(uint64 subId) external view returns (bool);
}
文件 24 的 24:WinningEntrySearchLogicV2.sol
pragma solidity 0.8.20;
contract WinningEntrySearchLogicV2 {
function _searchForWinningEntryUntilThereIsNotADuplicate(
uint256 randomWord,
uint256 currentEntryIndex,
uint256[] memory winningEntriesBitmap
)
internal
pure
returns (
uint256,
uint256,
uint256[] memory
)
{
uint256 winningEntry = randomWord % (currentEntryIndex + 1);
uint256 bucket = winningEntry >> 8;
uint256 mask = 1 << (winningEntry & 0xff);
while (winningEntriesBitmap[bucket] & mask != 0) {
randomWord = uint256(keccak256(abi.encodePacked(randomWord)));
winningEntry = randomWord % (currentEntryIndex + 1);
bucket = winningEntry >> 8;
mask = 1 << (winningEntry & 0xff);
}
winningEntriesBitmap[bucket] |= mask;
return (randomWord, winningEntry, winningEntriesBitmap);
}
}
{
"compilationTarget": {
"contracts/RaffleV2.sol": "RaffleV2"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 888888
},
"remappings": [
":@chainlink/=node_modules/@chainlink/",
":@ensdomains/=node_modules/@ensdomains/",
":@eth-optimism/=node_modules/@eth-optimism/",
":@looksrare/=node_modules/@looksrare/",
":@openzeppelin/=node_modules/@openzeppelin/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":eth-gas-reporter/=node_modules/eth-gas-reporter/",
":forge-std/=lib/forge-std/src/",
":hardhat/=node_modules/hardhat/"
],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"_weth","type":"address"},{"internalType":"bytes32","name":"_keyHash","type":"bytes32"},{"internalType":"uint64","name":"_subscriptionId","type":"uint64"},{"internalType":"address","name":"_vrfCoordinator","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_protocolFeeRecipient","type":"address"},{"internalType":"uint16","name":"_protocolFeeBp","type":"uint16"},{"internalType":"address","name":"_transferManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CutoffTimeNotReached","type":"error"},{"inputs":[],"name":"CutoffTimeReached","type":"error"},{"inputs":[],"name":"DrawExpirationTimeNotReached","type":"error"},{"inputs":[],"name":"ERC1155SafeTransferFromFail","type":"error"},{"inputs":[],"name":"ERC20TransferFail","type":"error"},{"inputs":[],"name":"ERC721TransferFromFail","type":"error"},{"inputs":[],"name":"InsufficientNativeTokensSupplied","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"InvalidCount","type":"error"},{"inputs":[],"name":"InvalidCurrency","type":"error"},{"inputs":[],"name":"InvalidCutoffTime","type":"error"},{"inputs":[],"name":"InvalidIndex","type":"error"},{"inputs":[],"name":"InvalidPricingOption","type":"error"},{"inputs":[],"name":"InvalidPricingOptionsCount","type":"error"},{"inputs":[],"name":"InvalidPrize","type":"error"},{"inputs":[],"name":"InvalidPrizesCount","type":"error"},{"inputs":[],"name":"InvalidProtocolFeeBp","type":"error"},{"inputs":[],"name":"InvalidProtocolFeeRecipient","type":"error"},{"inputs":[],"name":"InvalidStatus","type":"error"},{"inputs":[],"name":"InvalidWinnersCount","type":"error"},{"inputs":[],"name":"IsPaused","type":"error"},{"inputs":[],"name":"MaximumEntriesPerParticipantReached","type":"error"},{"inputs":[],"name":"MaximumEntriesReached","type":"error"},{"inputs":[],"name":"NoOngoingTransferInProgress","type":"error"},{"inputs":[],"name":"NotAContract","type":"error"},{"inputs":[],"name":"NotEnoughEntries","type":"error"},{"inputs":[],"name":"NotOwner","type":"error"},{"inputs":[],"name":"NotPaused","type":"error"},{"inputs":[],"name":"NothingToClaim","type":"error"},{"inputs":[{"internalType":"address","name":"have","type":"address"},{"internalType":"address","name":"want","type":"address"}],"name":"OnlyCoordinatorCanFulfill","type":"error"},{"inputs":[],"name":"RandomnessRequestAlreadyExists","type":"error"},{"inputs":[],"name":"RandomnessRequestDoesNotExist","type":"error"},{"inputs":[],"name":"ReentrancyFail","type":"error"},{"inputs":[],"name":"RenouncementNotInProgress","type":"error"},{"inputs":[],"name":"TransferAlreadyInProgress","type":"error"},{"inputs":[],"name":"TransferNotInProgress","type":"error"},{"inputs":[],"name":"WrongPotentialOwner","type":"error"},{"anonymous":false,"inputs":[],"name":"CancelOwnershipTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"currencies","type":"address[]"},{"indexed":false,"internalType":"bool","name":"isAllowed","type":"bool"}],"name":"CurrenciesStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint208","name":"amount","type":"uint208"}],"name":"EntryRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint40","name":"entriesCount","type":"uint40"},{"indexed":false,"internalType":"uint208","name":"price","type":"uint208"}],"name":"EntrySold","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesClaimed","type":"event"},{"anonymous":false,"inputs":[],"name":"InitiateOwnershipRenouncement","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":false,"internalType":"address","name":"potentialOwner","type":"address"}],"name":"InitiateOwnershipTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"winnerIndex","type":"uint256"}],"name":"PrizeClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"winnerIndex","type":"uint256[]"}],"name":"PrizesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"protocolFeeBp","type":"uint16"}],"name":"ProtocolFeeBpUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"protocolFeeRecipient","type":"address"}],"name":"ProtocolFeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"enum IRaffleV2.RaffleStatus","name":"status","type":"uint8"}],"name":"RaffleStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"raffleId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"RandomnessRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"MAXIMUM_NUMBER_OF_PRIZES_PER_RAFFLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAXIMUM_NUMBER_OF_WINNERS_PER_RAFFLE","outputs":[{"internalType":"uint40","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAXIMUM_PRICING_OPTIONS_PER_RAFFLE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAXIMUM_PROTOCOL_FEE_BP","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUBSCRIPTION_ID","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"cancelAfterRandomnessRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"},{"internalType":"uint256","name":"winnerIndex","type":"uint256"}],"name":"claimPrize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"raffleId","type":"uint256"},{"internalType":"uint256[]","name":"winnerIndices","type":"uint256[]"}],"internalType":"struct IRaffleV2.ClaimPrizesCalldata[]","name":"claimPrizesCalldata","type":"tuple[]"}],"name":"claimPrizes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"raffleIds","type":"uint256[]"}],"name":"claimRefund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmOwnershipRenouncement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"confirmOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint40","name":"cutoffTime","type":"uint40"},{"internalType":"bool","name":"isMinimumEntriesFixed","type":"bool"},{"internalType":"uint40","name":"minimumEntries","type":"uint40"},{"internalType":"uint40","name":"maximumEntriesPerParticipant","type":"uint40"},{"internalType":"uint16","name":"protocolFeeBp","type":"uint16"},{"internalType":"address","name":"feeTokenAddress","type":"address"},{"components":[{"internalType":"uint40","name":"winnersCount","type":"uint40"},{"internalType":"uint40","name":"cumulativeWinnersCount","type":"uint40"},{"internalType":"enum IRaffleV2.TokenType","name":"prizeType","type":"uint8"},{"internalType":"uint8","name":"prizeTier","type":"uint8"},{"internalType":"address","name":"prizeAddress","type":"address"},{"internalType":"uint256","name":"prizeId","type":"uint256"},{"internalType":"uint256","name":"prizeAmount","type":"uint256"}],"internalType":"struct IRaffleV2.Prize[]","name":"prizes","type":"tuple[]"},{"components":[{"internalType":"uint40","name":"entriesCount","type":"uint40"},{"internalType":"uint208","name":"price","type":"uint208"}],"internalType":"struct IRaffleV2.PricingOption[]","name":"pricingOptions","type":"tuple[]"}],"internalType":"struct IRaffleV2.CreateRaffleCalldata","name":"params","type":"tuple"}],"name":"createRaffle","outputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"drawWinners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"raffleId","type":"uint256"},{"internalType":"uint256","name":"pricingOptionIndex","type":"uint256"},{"internalType":"uint40","name":"count","type":"uint40"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct IRaffleV2.EntryCalldata[]","name":"entries","type":"tuple[]"}],"name":"enterRaffles","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"getEntries","outputs":[{"components":[{"internalType":"uint40","name":"currentEntryIndex","type":"uint40"},{"internalType":"address","name":"participant","type":"address"}],"internalType":"struct IRaffleV2.Entry[]","name":"entries","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"getPricingOptions","outputs":[{"components":[{"internalType":"uint40","name":"entriesCount","type":"uint40"},{"internalType":"uint208","name":"price","type":"uint208"}],"internalType":"struct IRaffleV2.PricingOption[]","name":"pricingOptions","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"getPrizes","outputs":[{"components":[{"internalType":"uint40","name":"winnersCount","type":"uint40"},{"internalType":"uint40","name":"cumulativeWinnersCount","type":"uint40"},{"internalType":"enum IRaffleV2.TokenType","name":"prizeType","type":"uint8"},{"internalType":"uint8","name":"prizeTier","type":"uint8"},{"internalType":"address","name":"prizeAddress","type":"address"},{"internalType":"uint256","name":"prizeId","type":"uint256"},{"internalType":"uint256","name":"prizeAmount","type":"uint256"}],"internalType":"struct IRaffleV2.Prize[]","name":"prizes","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"getWinners","outputs":[{"components":[{"internalType":"address","name":"participant","type":"address"},{"internalType":"bool","name":"claimed","type":"bool"},{"internalType":"uint8","name":"prizeIndex","type":"uint8"},{"internalType":"uint40","name":"entryIndex","type":"uint40"}],"internalType":"struct IRaffleV2.Winner[]","name":"winners","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initiateOwnershipRenouncement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newPotentialOwner","type":"address"}],"name":"initiateOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isCurrencyAllowed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","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":"ownershipStatus","outputs":[{"internalType":"enum IOwnableTwoSteps.Status","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"potentialOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeBp","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"raffles","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"enum IRaffleV2.RaffleStatus","name":"status","type":"uint8"},{"internalType":"bool","name":"isMinimumEntriesFixed","type":"bool"},{"internalType":"uint40","name":"cutoffTime","type":"uint40"},{"internalType":"uint40","name":"drawnAt","type":"uint40"},{"internalType":"uint40","name":"minimumEntries","type":"uint40"},{"internalType":"uint40","name":"maximumEntriesPerParticipant","type":"uint40"},{"internalType":"address","name":"feeTokenAddress","type":"address"},{"internalType":"uint16","name":"protocolFeeBp","type":"uint16"},{"internalType":"uint208","name":"claimableFees","type":"uint208"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rafflesCount","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"rafflesParticipantsStats","outputs":[{"internalType":"uint208","name":"amountPaid","type":"uint208"},{"internalType":"uint40","name":"entriesCount","type":"uint40"},{"internalType":"bool","name":"refunded","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"randomnessRequests","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint80","name":"raffleId","type":"uint80"},{"internalType":"uint256","name":"randomWord","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"},{"internalType":"uint256[]","name":"randomWords","type":"uint256[]"}],"name":"rawFulfillRandomWords","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"refundableRaffleIds","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"raffleId","type":"uint256"},{"internalType":"uint256","name":"pricingOptionIndex","type":"uint256"},{"internalType":"uint40","name":"count","type":"uint40"},{"internalType":"address","name":"recipient","type":"address"}],"internalType":"struct IRaffleV2.EntryCalldata[]","name":"entries","type":"tuple[]"}],"name":"rollover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"name":"selectWinners","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_protocolFeeBp","type":"uint16"}],"name":"setProtocolFeeBp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_protocolFeeRecipient","type":"address"}],"name":"setProtocolFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"togglePaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"currencies","type":"address[]"},{"internalType":"bool","name":"isAllowed","type":"bool"}],"name":"updateCurrenciesStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"raffleId","type":"uint256"}],"name":"withdrawPrizes","outputs":[],"stateMutability":"nonpayable","type":"function"}]