Source Code
Overview
ETH Balance
0 ETH
Token Holdings
More Info
ContractCreator
Multichain Info
N/A
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
Contract Name:
RaffleV2
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 888888 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
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";
// ....................................................................................................
// .......................................,,,,,,.......................................................
// ....................................,;+??????*;;:..................,,,,,,,,.........................
// ..................................,+?????????????*;,............:;*????????*;,......................
// ................................,+??????????????*???+,.......:+*??????????????*:....................
// ..............................:+?????????*????????*???+:,.,;*???????????????????*,..................
// ............................,*???????????%%%%%%%%%%??*???*???????????????????????*,.................
// ..........................,;???????????%%??????????%%%?*????????????????????????*?;.................
// .........................:???????????%%???????????????%%???????*?????????????*?**?*.................
// ........................:%%%???????%%?*??????????????**?%?**???%%%%%%%%%%%%%%??????,,,,.............
// ......................,*SSSSS?????%?*??????????*??????%??%%??????????????***???????????*;:..........
// .....................:%S%%%%SS??????????????*??%%%%%%%?????%%%%??*????????????????????????+,........
// ....................:SS%%%%%%#?*????????????%%%????**???????*??%%%?*??%%%%%%%?????????%%%%%:........
// ....................+S?%%%%%%S%*?????????*?%%?**??%%%%%%%%%%%??*?%??%%??????????%%%%%?????%?+,......
// ..................,;%%%%%%%%%%S*???????*??%?*??%%%???????????%%%??S%????%%%%%%???????%%%%????*,.....
// .................;?S#%%%%?SS%%S*???????%%%?*?%%??*??****????????%%%?%%%?????????*++++*?%%%%%??:.....
// ...............,*%?S%%%%%%S%%SS*????%%%????%%??*????%SSS##S***?????%???*????*;,,:::;:,,:+???%S+.....
// ..............,*?*?S%%%%%%#SSS?????%???*?%%???????%@@@@@@##S:.,:+????????+:,..:%@@@%@#*,..:*???+....
// ..............+????S%%%%%%S%???????????%%??*???+:+#@@@@@SS@@#:...,;????+,....;@@@@@%@@@#+...:???:...
// .............:?????S%%%%%%S*????%%%%%%%??????+,.,#+S@@@;.+@@@%.....+%*,.....,S@@@@@?;*#@@+...,+?,...
// .............*?????S%%%%%%S*????%**???**???+,...?@#@@@S..,#@@@;....;*.......+%?@@@%...;@@#,....;:...
// ............;??????S%%%%%%S?????%%???????+,....,#@@@@@#:.,#@@@%....;,.......S@@@@@*....#@@+....,;...
// ...........,???????S%%%%%%S????????%%%%:,......:@@@@@@@S*%@@@@#,..,;.......,#@@@@@?....S@@%....:;...
// ...........;???????%S%%%%%SS*??????**???:......:@@@@@@@@@@@@@@@+..::.......,@@@@@@#;,.;@@@#,..,+,...
// ..........,?????????S%%%%%%S?????????????*:....,#@@@@@@@@@@@@@@?.,+:.......,@@@@@@@@#S@@@@@:.;+,....
// ..........;????????*S%%%%%%SS*?????????????*:...S@@@@@@@@@@@@@@S+?%*::,....,#@@@@@@@@@@@@@@?*%;.....
// ..........*????????*SS%S#S?%S??????????%?**??*;,*@@@@@@@@@@@@#S%???%???**+;:S@@@@@@@@##S%?????:.....
// .........:?????????*%#S%%S%%%S???*??????%%%??????####@@@@#S%?***?%%?????????%%%%??????*****?*:......
// .........*?????????*%#?*%S%%%S?*?%%%%%%%%?%%%%%%%%???????****??%%%????????????????????????%;........
// ........:?????????*%#?**S%?%%%S%%%?????%%%%%????????%%%%%%%%%%%?????????????%%%%%%%%%%%%%%+.........
// ........*??????????#%*?*%SSSS%S#???????????%%%%%??***???????????????????????*??*??*?????*%+:........
// .......:?????????*%#?????%%%S%%S%??????????????%%%%%???***??????????????????????????**??%%%%+.......
// .......*??????????#%*???***S%%%%#????S#S%%?????????%%%%%%?????*?????????????????**???%%%%????.......
// ......:?????????*SS*???????SSSS%SS??SS%S#%%%%???????????%%%%%%%%?????????????????%%%%%???????.......
// ......+????????*?#????????????%S%SSSS%%%SS?%%%%%%?????????????%%%%%%%%%%%%%%%%%%%%?????????%+.......
// ......*?????????#S??????????*?S%%%SS%%%%%S%????%%%%%?????????????????????????????????????%%+,.......
// ......*???????*%#SS?????????*%%%%%%%%%%%%%#???????%%%%%%%?????????????????????????????%%?+:.........
// ......*????????SS%SS?*?????*%%%%%%%%%%%%%%SS???????????%%%%%%%??????????????????%%%%%%%%*...........
// .....;S%???????#%%%SS?*???*%#%%%%%%%%%%%%%%#%%?????????????%%%%%%%%%%%%%%%%%%%%%%%%??????,..........
// ...,?SSS%*???*SS%%%%S#?*?*%#%#%%%%%%%%%%%%%S?%%%%????????????????????????????????????????,..........
// ...?S%%%S?*???S%%%%%%S#?*?#?*%SS%%%%%%%%%%%S?*??%%%%???????????????????????????????????%?...........
// .,?S%%%%SS%?*%S%%%%%%%SS?S%*?*?SS%%%%%%%%%%S????*??%%%%%%%????????????????????????????%%:...........
// .*#%%%%%%%SS%S%%%%%%%%%S#S??????%S#S%%%%?%SS???????*?????%%%%%%%%%%%%%%%%%??????????%%*:............
// ;S%%%%%%%%%S#S%%%%%%%%%S#????????*?SSSSSSS#??????????????*?????????%%%%%%%%%%%%%S%??+,..............
// %S%%%%%%%%%SS%%%%%%%%%%S%*????????**%%%%%##????????????????????????*******??????+,..................
// S%%%%%%%%%SS%%%%%%%%%%S#%%?????**???****?SS%*??????????????????????????????**+:,....................
// %%%%%%%%S#S%%%%%%%%%%%S???%%%%%%?????????SSS*??????????????????***??+;;;::,.........................
// %%%%%%%S#S%%%%%%%%%%%SS?***??????%%%%%%%%SSS?????????????????????%?:................................
// %%%%%%SSS%%%%%%%%%%%S#SS%????*****???????%SS%%%%%%%%%%%%%%%%%??%%+,.................................
// %%%%%%%%%%%%%%%%%%%%SS%SSSSSS%%????******%SS?************?***?%*:...................................
// %%%%%%%%%%%%%%%%%%%%#S%%%%%%SSSSSSS%%%???%SS%???????????????%S#+....................................
// %%%%%%%%%%%%%%%%%%%S#%%%%%%%%%%%%%SSSSSSSS#%#SSSSSSSSSSSSSSSS%S%,...................................
// %%%%%%%%%%%%%%%%%%%#S%%%%%%%%%%%%%%%%%S####%#%%%%%%%%%%%%%%%%%%S+...................................
// %%%%%%%%%%%%%%%%%%S#%%%%%%%%%%%%%%%%%%#S%S@%#S%%%%%%%%%%%%%%%%%S%,..................................
// %%%%%%%%%%%%%%%%%%#S%%%%%%%%%%%%%%%%%SS%#S#%SS%%%%%%%%%%%%%%%%%%S*..................................
// %%%%%%%%%%%%%%%%%S#S%%%%%%%%%%%%%%%%%SSSS##S%#%%%%%%%%%%%%%%%%%%%S:.................................
// %%%%%%%%%%%%%%%%%SS%%%%%%%%%%%%%%%%%%%#####%%#%%%%%%%%%%%%%%%%%%%S*.................................
// %%%%%%%%%%%%%%%%%#S%%%%%%%%%%%%%%%%%%%S#S%%?S#%%%%%%%%%%%%%%%%%%%SS,................................
// %%%%%%%%%%%%%%%%S#S%%%%%%%%%%%%%%%%%%%%S#SS##S%%%%%%%%%%%%%%%%%%%%S;................................
// %%%%%%%%%%%%%%%%SS%%%%%%%%%%%%%%%%%%%%%%SS##S%%%%%%%%%%%%%%%%%%%%%S?................................
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%SSS%%%%%%%%%%%%%%%%%%%%%S%,...............................
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#SS%%%%%%%%%%%%%%%%%%%%%%S:...............................
// ...................... [Calling the blockchain to get provably fair results] .......................
/**
* @title RaffleV2
* @notice This contract allows anyone to permissionlessly host raffles on LooksRare.
* @author LooksRare protocol team (👀,💎)
*/
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;
/**
* @notice 100% in basis points.
*/
uint256 private constant ONE_HUNDRED_PERCENT_BP = 10_000;
/**
* @notice The raffles created.
* @dev The key is the raffle ID.
*/
mapping(uint256 => Raffle) public raffles;
/**
* @notice The participants stats of the raffles.
* @dev The key is the raffle ID and the nested key is the participant address.
*/
mapping(uint256 => mapping(address => ParticipantStats)) public rafflesParticipantsStats;
/**
* @notice It checks whether the currency is allowed.
* @dev 0 is not allowed, 1 is allowed.
*/
mapping(address => uint256) public isCurrencyAllowed;
/**
* @notice The maximum number of prizes per raffle.
* Each individual ERC-721 counts as one prize.
* Each ETH/ERC-20/ERC-1155 with winnersCount > 1 counts as one prize.
*/
uint256 public constant MAXIMUM_NUMBER_OF_PRIZES_PER_RAFFLE = 200;
/**
* @notice The maximum number of winners per raffle.
*/
uint40 public constant MAXIMUM_NUMBER_OF_WINNERS_PER_RAFFLE = 200;
/**
* @notice The key hash of the Chainlink VRF.
*/
bytes32 private immutable KEY_HASH;
/**
* @notice The subscription ID of the Chainlink VRF.
*/
uint64 public immutable SUBSCRIPTION_ID;
/**
* @notice The Chainlink VRF coordinator.
*/
VRFCoordinatorV2Interface private immutable VRF_COORDINATOR;
/**
* @notice The randomness requests.
* @dev The key is the request ID returned by Chainlink.
*/
mapping(uint256 => RandomnessRequest) public randomnessRequests;
/**
* @notice The maximum protocol fee in basis points, which is 25%.
*/
uint16 public constant MAXIMUM_PROTOCOL_FEE_BP = 2_500;
/**
* @notice The number of raffles created.
* @dev In this smart contract, raffleId is an uint256 but its
* max value can only be 2^80 - 1. Realistically we will still
* not reach this number.
*/
uint80 public rafflesCount;
/**
* @notice The protocol fee recipient.
*/
address public protocolFeeRecipient;
/**
* @notice The protocol fee in basis points.
*/
uint16 public protocolFeeBp;
/**
* @notice The maximum number of pricing options per raffle.
*/
uint256 public constant MAXIMUM_PRICING_OPTIONS_PER_RAFFLE = 5;
/**
* @notice Transfer manager faciliates token transfers.
*/
ITransferManager private immutable transferManager;
/**
* @param _weth The WETH address
* @param _keyHash Chainlink VRF key hash
* @param _subscriptionId Chainlink VRF subscription ID
* @param _vrfCoordinator Chainlink VRF coordinator address
* @param _owner The owner of the contract
* @param _protocolFeeRecipient The recipient of the protocol fees
* @param _protocolFeeBp The protocol fee in basis points
* @param _transferManager The transfer manager address
*/
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);
}
/**
* @inheritdoc IRaffleV2
*/
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;
}
// The storage layout of a prize struct (3 slots) is as follows:
// --------------------------------------------------------------------------------------------------------------------------------|
// | prizeAddress (160 bits) | prizeTier (8 bits) | prizeType (8 bits) | cumulativeWinnersCount (40 bits) | winnersCount (40 bits) |
// --------------------------------------------------------------------------------------------------------------------------------|
// | prizeId (256 bits) |
// --------------------------------------------------------------------------------------------------------------------------------|
// | prizeAmount (256 bits) |
//
// The slot keccak256(raffleId, rafflesSlot) + 4 is used to store the length of the prizes array.
// The slot keccak256(keccak256(raffleId, rafflesSlot) + 4) + i * 3 is used to store the prize at the i-th index
// (x 3 because each prize consumes 3 slots).
//
// The assembly blocks are equivalent to `raffle.prizes.push(prize);`
//
// The primary benefit of using assembly is we only write the prizes length once instead of once per prize.
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;
// The storage layout of a raffle's first 2 slots is as follows:
// ---------------------------------------------------------------------------------------------------------------------------------|
// | drawnAt (40 bits) | cutoffTime (40 bits) | isMinimumEntriesFixed (8 bits) | status (8 bits) | owner (160 bits) |
// ---------------------------------------------------------------------------------------------------------------------------------|
// | agreedProtocolFeeBp (16 bits) | feeTokenAddress (160 bits) | maximumEntriesPerParticipant (40 bits) | minimumEntries (40 bits) |
// ---------------------------------------------------------------------------------------------------------------------------------|
//
// And the slots for these values are calculated by the following formulas:
// slot 1 = keccak256(raffleId, rafflesSlot)
// slot 2 = keccak256(raffleId, rafflesSlot) + 1
//
// This assembly block is equivalent to
// raffle.owner = msg.sender;
// raffle.status = RaffleStatus.Open;
// raffle.isMinimumEntriesFixed = isMinimumEntriesFixed;
// raffle.cutoffTime = cutoffTime;
// raffle.minimumEntries = minimumEntries;
// raffle.maximumEntriesPerParticipant = maximumEntriesPerParticipant;
// raffle.protocolFeeBp = agreedProtocolFeeBp;
// raffle.feeTokenAddress = feeTokenAddress;
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);
}
/**
* @dev This function is required in order for the contract to receive ERC-1155 tokens.
*/
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes calldata
) external pure returns (bytes4) {
return this.onERC1155Received.selector;
}
/**
* @inheritdoc IRaffleV2
* @notice If it is a delegated recipient, the amount paid should still be accrued to the payer.
* If a raffle is cancelled, the payer should be refunded and not the recipient.
*/
function enterRaffles(EntryCalldata[] calldata entries) external payable nonReentrant whenNotPaused {
(address feeTokenAddress, uint208 expectedValue) = _enterRaffles(entries);
_chargeUser(feeTokenAddress, expectedValue);
}
/**
* @param _requestId The ID of the request
* @param _randomWords The random words returned by Chainlink
*/
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];
}
}
}
/**
* @inheritdoc IRaffleV2
*/
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;
// The storage layout of a winner slot is as follows:
// ------------------------------------------------------------------------------------------------------------|
// | unused (40 bits) | entryIndex (40 bits) | prizeIndex (8 bits) | claimed (8 bits) | participant (160 bits) |
// ------------------------------------------------------------------------------------------------------------|
//
// The slot keccak256(raffleId, rafflesSlot) + 6 is used to store the length of the winners array.
// The slot keccak256(keccak256(raffleId, rafflesSlot) + 6) + i is used to store the winner at the i-th index.
//
// The assembly blocks are equivalent to
// raffle.winners.push(
// Winner({
// participant: entries[currentEntryIndexArray.findUpperBound(winningEntry)].participant,
// claimed: false,
// prizeIndex: uint8(cumulativeWinnersCountArray.findUpperBound(_unsafeAdd(i, 1))),
// entryIndex: uint40(winningEntry)
// })
// );
//
// The primary benefit of using assembly is we only write the winners length once instead of once per winner.
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)
}
}
/**
* @inheritdoc IRaffleV2
*/
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);
}
/**
* @inheritdoc IRaffleV2
*/
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);
}
}
/**
* @inheritdoc IRaffleV2
*/
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);
}
/**
* @inheritdoc IRaffleV2
*/
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);
}
/**
* @inheritdoc IRaffleV2
* @notice A raffle cannot be drawn if there are less entries than prizes.
*/
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);
}
/**
* @inheritdoc IRaffleV2
*/
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);
}
/**
* @inheritdoc IRaffleV2
*/
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;
}
}
}
/**
* @inheritdoc IRaffleV2
* @dev Refundable and Cancelled are the only statuses that allow refunds.
*/
function claimRefund(uint256[] calldata raffleIds) external nonReentrant whenNotPaused {
(address feeTokenAddress, uint208 refundAmount) = _claimRefund(raffleIds);
_transferFungibleTokens(feeTokenAddress, msg.sender, refundAmount);
}
/**
* @inheritdoc IRaffleV2
* @notice The fee token address for all the raffles involved must be the same.
* @dev Refundable and Cancelled are the only statuses that allow refunds.
*/
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));
}
}
/**
* @inheritdoc IRaffleV2
*/
function setProtocolFeeRecipient(address _protocolFeeRecipient) external onlyOwner {
_setProtocolFeeRecipient(_protocolFeeRecipient);
}
/**
* @inheritdoc IRaffleV2
*/
function setProtocolFeeBp(uint16 _protocolFeeBp) external onlyOwner {
_setProtocolFeeBp(_protocolFeeBp);
}
/**
* @inheritdoc IRaffleV2
*/
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);
}
/**
* @inheritdoc IRaffleV2
*/
function togglePaused() external onlyOwner {
paused() ? _unpause() : _pause();
}
/**
* @inheritdoc IRaffleV2
*/
function getWinners(uint256 raffleId) external view returns (Winner[] memory winners) {
winners = raffles[raffleId].winners;
}
/**
* @inheritdoc IRaffleV2
*/
function getPrizes(uint256 raffleId) external view returns (Prize[] memory prizes) {
prizes = raffles[raffleId].prizes;
}
/**
* @inheritdoc IRaffleV2
*/
function getEntries(uint256 raffleId) external view returns (Entry[] memory entries) {
entries = raffles[raffleId].entries;
}
/**
* @inheritdoc IRaffleV2
*/
function getPricingOptions(uint256 raffleId) external view returns (PricingOption[] memory pricingOptions) {
pricingOptions = raffles[raffleId].pricingOptions;
}
/**
* @param _protocolFeeRecipient The new protocol fee recipient address
*/
function _setProtocolFeeRecipient(address _protocolFeeRecipient) private {
if (_protocolFeeRecipient == address(0)) {
revert InvalidProtocolFeeRecipient();
}
protocolFeeRecipient = _protocolFeeRecipient;
emit ProtocolFeeRecipientUpdated(_protocolFeeRecipient);
}
/**
* @param _protocolFeeBp The new protocol fee in basis points
*/
function _setProtocolFeeBp(uint16 _protocolFeeBp) private {
if (_protocolFeeBp > MAXIMUM_PROTOCOL_FEE_BP) {
revert InvalidProtocolFeeBp();
}
protocolFeeBp = _protocolFeeBp;
emit ProtocolFeeBpUpdated(_protocolFeeBp);
}
/**
* @param raffleId The ID of the raffle.
* @param pricingOptions The pricing options for the raffle.
*/
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;
// The storage layout of a pricing option slot is as follows:
// ---------------------------------------------------------------|
// | unused (8 bits) | price (208 bits) | entries count (40 bits) |
// ---------------------------------------------------------------|
//
// The slot keccak256(raffleId, rafflesSlot) + 3 is used to store the length of the pricing options array.
// The slot keccak256(keccak256(raffleId, rafflesSlot) + 3) + i is used to store the pricing option at the i-th index.
//
// The assembly blocks are equivalent to `raffles[raffleId].pricingOptions.push(pricingOption);`
//
// The primary benefit of using assembly is we only write the pricing options length once instead of once per pricing option.
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)
}
}
/**
* @param prize The prize.
*/
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();
}
}
}
/**
* @param prize The prize to transfer.
* @param recipient The recipient of the prize.
* @param multiplier The multiplier to apply to the prize amount.
*/
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);
}
}
/**
* @param currency The currency to transfer.
* @param recipient The recipient of the currency.
* @param amount The amount of currency to transfer.
*/
function _transferFungibleTokens(
address currency,
address recipient,
uint256 amount
) private {
if (currency == address(0)) {
_transferETHAndWrapIfFailWithGasLimit(WETH, recipient, amount, gasleft());
} else {
_executeERC20DirectTransfer(currency, recipient, amount);
}
}
/**
* @param transferAccumulator The transfer accumulator.
*/
function _transferFungibleTokens(TransferAccumulator memory transferAccumulator) private {
_transferFungibleTokens(transferAccumulator.tokenAddress, msg.sender, transferAccumulator.amount);
}
/**
* @param raffleId The ID of the raffle to draw winners for.
* @param raffle The raffle to draw winners for.
*/
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);
}
/**
* @param raffle The raffle to check the status of.
* @param status The expected status of the raffle
*/
function _validateRaffleStatus(Raffle storage raffle, RaffleStatus status) private view {
if (raffle.status != status) {
revert InvalidStatus();
}
}
/**
* @param entries The entries to enter.
*/
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;
}
}
}
/**
* @param feeTokenAddress The address of the token to charge the fee in.
* @param expectedValue The expected value of the fee.
*/
function _chargeUser(address feeTokenAddress, uint256 expectedValue) private {
if (feeTokenAddress == address(0)) {
_validateExpectedEthValueOrRefund(expectedValue);
} else {
transferManager.transferERC20(feeTokenAddress, msg.sender, address(this), expectedValue);
}
}
/**
* @param raffleIds The IDs of the raffles to claim refunds for.
*/
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;
}
}
}
/**
* @param caller The expected caller.
*/
function _validateCaller(address caller) private view {
if (msg.sender != caller) {
revert InvalidCaller();
}
}
/**
* @param currency The currency to validate.
*/
function _validateCurrency(address currency) private view {
if (isCurrencyAllowed[currency] != 1) {
revert InvalidCurrency();
}
}
/**
* @param expectedEthValue The expected ETH value to be sent by the caller.
*/
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()
);
}
}
/**
* @param raffle The raffle to validate.
*/
function _validateRafflePostCutoffTimeStatusTransferability(Raffle storage raffle) private view {
_validateRaffleStatus(raffle, RaffleStatus.Open);
if (raffle.cutoffTime > block.timestamp) {
revert CutoffTimeNotReached();
}
}
/**
* @param raffle The raffle to set the status of.
* @param raffleId The ID of the raffle to set the status of.
* @param status The status to set.
*/
function _setRaffleStatus(
Raffle storage raffle,
uint256 raffleId,
RaffleStatus status
) private {
raffle.status = status;
emit RaffleStatusUpdated(raffleId, status);
}
/**
* @param raffle The raffle to add the entry to.
* @param currentEntryIndex The cumulative number of entries in the raffle minus one.
* @param recipient The recipient of the entry.
*/
function _pushEntry(
Raffle storage raffle,
uint40 currentEntryIndex,
address recipient
) private {
raffle.entries.push(Entry({currentEntryIndex: currentEntryIndex, participant: recipient}));
}
/**
* Unsafe math functions.
*/
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;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IWETH} from "../interfaces/generic/IWETH.sol";
/**
* @title LowLevelWETH
* @notice This contract contains a function to transfer ETH with an option to wrap to WETH.
* If the ETH transfer fails within a gas limit, the amount in ETH is wrapped to WETH and then transferred.
* @author LooksRare protocol team (👀,💎)
*/
contract LowLevelWETH {
/**
* @notice It transfers ETH to a recipient with a specified gas limit.
* If the original transfers fails, it wraps to WETH and transfers the WETH to recipient.
* @param _WETH WETH address
* @param _to Recipient address
* @param _amount Amount to transfer
* @param _gasLimit Gas limit to perform the ETH transfer
*/
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);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IERC20} from "../interfaces/generic/IERC20.sol";
// Errors
import {ERC20TransferFail, ERC20TransferFromFail} from "../errors/LowLevelErrors.sol";
import {NotAContract} from "../errors/GenericErrors.sol";
/**
* @title LowLevelERC20Transfer
* @notice This contract contains low-level calls to transfer ERC20 tokens.
* @author LooksRare protocol team (👀,💎)
*/
contract LowLevelERC20Transfer {
/**
* @notice Execute ERC20 transferFrom
* @param currency Currency address
* @param from Sender address
* @param to Recipient address
* @param amount Amount to transfer
*/
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();
}
}
}
/**
* @notice Execute ERC20 (direct) transfer
* @param currency Currency address
* @param to Recipient address
* @param amount Amount to transfer
*/
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();
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IERC721} from "../interfaces/generic/IERC721.sol";
// Errors
import {ERC721TransferFromFail} from "../errors/LowLevelErrors.sol";
import {NotAContract} from "../errors/GenericErrors.sol";
/**
* @title LowLevelERC721Transfer
* @notice This contract contains low-level calls to transfer ERC721 tokens.
* @author LooksRare protocol team (👀,💎)
*/
contract LowLevelERC721Transfer {
/**
* @notice Execute ERC721 transferFrom
* @param collection Address of the collection
* @param from Address of the sender
* @param to Address of the recipient
* @param tokenId tokenId to transfer
*/
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();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IERC1155} from "../interfaces/generic/IERC1155.sol";
// Errors
import {ERC1155SafeTransferFromFail, ERC1155SafeBatchTransferFromFail} from "../errors/LowLevelErrors.sol";
import {NotAContract} from "../errors/GenericErrors.sol";
/**
* @title LowLevelERC1155Transfer
* @notice This contract contains low-level calls to transfer ERC1155 tokens.
* @author LooksRare protocol team (👀,💎)
*/
contract LowLevelERC1155Transfer {
/**
* @notice Execute ERC1155 safeTransferFrom
* @param collection Address of the collection
* @param from Address of the sender
* @param to Address of the recipient
* @param tokenId tokenId to transfer
* @param amount Amount to transfer
*/
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();
}
}
/**
* @notice Execute ERC1155 safeBatchTransferFrom
* @param collection Address of the collection
* @param from Address of the sender
* @param to Address of the recipient
* @param tokenIds Array of tokenIds to transfer
* @param amounts Array of amounts to transfer
*/
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();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IOwnableTwoSteps} from "./interfaces/IOwnableTwoSteps.sol";
/**
* @title OwnableTwoSteps
* @notice This contract offers transfer of ownership in two steps with potential owner
* having to confirm the transaction to become the owner.
* Renouncement of the ownership is also a two-step process since the next potential owner is the address(0).
* @author LooksRare protocol team (👀,💎)
*/
abstract contract OwnableTwoSteps is IOwnableTwoSteps {
/**
* @notice Address of the current owner.
*/
address public owner;
/**
* @notice Address of the potential owner.
*/
address public potentialOwner;
/**
* @notice Ownership status.
*/
Status public ownershipStatus;
/**
* @notice Modifier to wrap functions for contracts that inherit this contract.
*/
modifier onlyOwner() {
_onlyOwner();
_;
}
/**
* @notice Constructor
* @param _owner The contract's owner
*/
constructor(address _owner) {
owner = _owner;
emit NewOwner(_owner);
}
/**
* @notice This function is used to cancel the ownership transfer.
* @dev This function can be used for both cancelling a transfer to a new owner and
* cancelling the renouncement of the ownership.
*/
function cancelOwnershipTransfer() external onlyOwner {
Status _ownershipStatus = ownershipStatus;
if (_ownershipStatus == Status.NoOngoingTransfer) {
revert NoOngoingTransferInProgress();
}
if (_ownershipStatus == Status.TransferInProgress) {
delete potentialOwner;
}
delete ownershipStatus;
emit CancelOwnershipTransfer();
}
/**
* @notice This function is used to confirm the ownership renouncement.
*/
function confirmOwnershipRenouncement() external onlyOwner {
if (ownershipStatus != Status.RenouncementInProgress) {
revert RenouncementNotInProgress();
}
delete owner;
delete ownershipStatus;
emit NewOwner(address(0));
}
/**
* @notice This function is used to confirm the ownership transfer.
* @dev This function can only be called by the current potential owner.
*/
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);
}
/**
* @notice This function is used to initiate the transfer of ownership to a new owner.
* @param newPotentialOwner New potential owner address
*/
function initiateOwnershipTransfer(address newPotentialOwner) external onlyOwner {
if (ownershipStatus != Status.NoOngoingTransfer) {
revert TransferAlreadyInProgress();
}
ownershipStatus = Status.TransferInProgress;
potentialOwner = newPotentialOwner;
/**
* @dev This function can only be called by the owner, so msg.sender is the owner.
* We don't have to SLOAD the owner again.
*/
emit InitiateOwnershipTransfer(msg.sender, newPotentialOwner);
}
/**
* @notice This function is used to initiate the ownership renouncement.
*/
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();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
// Interfaces
import {IReentrancyGuard} from "./interfaces/IReentrancyGuard.sol";
/**
* @title PackableReentrancyGuard
* @notice This contract protects against reentrancy attacks.
* It is adjusted from OpenZeppelin.
* The only difference between this contract and ReentrancyGuard
* is that _status is uint8 instead of uint256 so that it can be
* packed with other contracts' storage variables.
* @author LooksRare protocol team (👀,💎)
*/
abstract contract PackableReentrancyGuard is IReentrancyGuard {
uint8 private _status;
/**
* @notice Modifier to wrap functions to prevent reentrancy calls.
*/
modifier nonReentrant() {
if (_status == 2) {
revert ReentrancyFail();
}
_status = 2;
_;
_status = 1;
}
constructor() {
_status = 1;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title Pausable
* @notice This contract makes it possible to pause the contract.
* It is adjusted from OpenZeppelin.
* @author LooksRare protocol team (👀,💎)
*/
abstract contract Pausable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
error IsPaused();
error NotPaused();
bool private _paused;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert IsPaused();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert NotPaused();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(msg.sender);
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(msg.sender);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
// Enums
import {TokenType} from "../enums/TokenType.sol";
/**
* @title ITransferManager
* @author LooksRare protocol team (👀,💎)
*/
interface ITransferManager {
/**
* @notice This struct is only used for transferBatchItemsAcrossCollections.
* @param tokenAddress Token address
* @param tokenType 0 for ERC721, 1 for ERC1155
* @param itemIds Array of item ids to transfer
* @param amounts Array of amounts to transfer
*/
struct BatchTransferItem {
address tokenAddress;
TokenType tokenType;
uint256[] itemIds;
uint256[] amounts;
}
/**
* @notice It is emitted if operators' approvals to transfer NFTs are granted by a user.
* @param user Address of the user
* @param operators Array of operator addresses
*/
event ApprovalsGranted(address user, address[] operators);
/**
* @notice It is emitted if operators' approvals to transfer NFTs are revoked by a user.
* @param user Address of the user
* @param operators Array of operator addresses
*/
event ApprovalsRemoved(address user, address[] operators);
/**
* @notice It is emitted if a new operator is added to the global allowlist.
* @param operator Operator address
*/
event OperatorAllowed(address operator);
/**
* @notice It is emitted if an operator is removed from the global allowlist.
* @param operator Operator address
*/
event OperatorRemoved(address operator);
/**
* @notice It is returned if the operator to approve has already been approved by the user.
*/
error OperatorAlreadyApprovedByUser();
/**
* @notice It is returned if the operator to revoke has not been previously approved by the user.
*/
error OperatorNotApprovedByUser();
/**
* @notice It is returned if the transfer caller is already allowed by the owner.
* @dev This error can only be returned for owner operations.
*/
error OperatorAlreadyAllowed();
/**
* @notice It is returned if the operator to approve is not in the global allowlist defined by the owner.
* @dev This error can be returned if the user tries to grant approval to an operator address not in the
* allowlist or if the owner tries to remove the operator from the global allowlist.
*/
error OperatorNotAllowed();
/**
* @notice It is returned if the transfer caller is invalid.
* For a transfer called to be valid, the operator must be in the global allowlist and
* approved by the 'from' user.
*/
error TransferCallerInvalid();
/**
* @notice This function transfers ERC20 tokens.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param amount amount
*/
function transferERC20(
address tokenAddress,
address from,
address to,
uint256 amount
) external;
/**
* @notice This function transfers a single item for a single ERC721 collection.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param itemId Item ID
*/
function transferItemERC721(
address tokenAddress,
address from,
address to,
uint256 itemId
) external;
/**
* @notice This function transfers items for a single ERC721 collection.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param itemIds Array of itemIds
* @param amounts Array of amounts
*/
function transferItemsERC721(
address tokenAddress,
address from,
address to,
uint256[] calldata itemIds,
uint256[] calldata amounts
) external;
/**
* @notice This function transfers a single item for a single ERC1155 collection.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param itemId Item ID
* @param amount Amount
*/
function transferItemERC1155(
address tokenAddress,
address from,
address to,
uint256 itemId,
uint256 amount
) external;
/**
* @notice This function transfers items for a single ERC1155 collection.
* @param tokenAddress Token address
* @param from Sender address
* @param to Recipient address
* @param itemIds Array of itemIds
* @param amounts Array of amounts
* @dev It does not allow batch transferring if from = msg.sender since native function should be used.
*/
function transferItemsERC1155(
address tokenAddress,
address from,
address to,
uint256[] calldata itemIds,
uint256[] calldata amounts
) external;
/**
* @notice This function transfers items across an array of tokens that can be ERC20, ERC721 and ERC1155.
* @param items Array of BatchTransferItem
* @param from Sender address
* @param to Recipient address
*/
function transferBatchItemsAcrossCollections(
BatchTransferItem[] calldata items,
address from,
address to
) external;
/**
* @notice This function allows a user to grant approvals for an array of operators.
* Users cannot grant approvals if the operator is not allowed by this contract's owner.
* @param operators Array of operator addresses
* @dev Each operator address must be globally allowed to be approved.
*/
function grantApprovals(address[] calldata operators) external;
/**
* @notice This function allows a user to revoke existing approvals for an array of operators.
* @param operators Array of operator addresses
* @dev Each operator address must be approved at the user level to be revoked.
*/
function revokeApprovals(address[] calldata operators) external;
/**
* @notice This function allows an operator to be added for the shared transfer system.
* Once the operator is allowed, users can grant NFT approvals to this operator.
* @param operator Operator address to allow
* @dev Only callable by owner.
*/
function allowOperator(address operator) external;
/**
* @notice This function allows the user to remove an operator for the shared transfer system.
* @param operator Operator address to remove
* @dev Only callable by owner.
*/
function removeOperator(address operator) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface VRFCoordinatorV2Interface {
/**
* @notice Get configuration relevant for making requests
* @return minimumRequestConfirmations global min for request confirmations
* @return maxGasLimit global max for request gas limit
* @return s_provingKeyHashes list of registered key hashes
*/
function getRequestConfig()
external
view
returns (
uint16,
uint32,
bytes32[] memory
);
/**
* @notice Request a set of random words.
* @param keyHash - Corresponds to a particular oracle job which uses
* that key for generating the VRF proof. Different keyHash's have different gas price
* ceilings, so you can select a specific one to bound your maximum per request cost.
* @param subId - The ID of the VRF subscription. Must be funded
* with the minimum subscription balance required for the selected keyHash.
* @param minimumRequestConfirmations - How many blocks you'd like the
* oracle to wait before responding to the request. See SECURITY CONSIDERATIONS
* for why you may want to request more. The acceptable range is
* [minimumRequestBlockConfirmations, 200].
* @param callbackGasLimit - How much gas you'd like to receive in your
* fulfillRandomWords callback. Note that gasleft() inside fulfillRandomWords
* may be slightly less than this amount because of gas used calling the function
* (argument decoding etc.), so you may need to request slightly more than you expect
* to have inside fulfillRandomWords. The acceptable range is
* [0, maxGasLimit]
* @param numWords - The number of uint256 random values you'd like to receive
* in your fulfillRandomWords callback. Note these numbers are expanded in a
* secure way by the VRFCoordinator from a single random value supplied by the oracle.
* @return requestId - A unique identifier of the request. Can be used to match
* a request to a response in fulfillRandomWords.
*/
function requestRandomWords(
bytes32 keyHash,
uint64 subId,
uint16 minimumRequestConfirmations,
uint32 callbackGasLimit,
uint32 numWords
) external returns (uint256 requestId);
/**
* @notice Create a VRF subscription.
* @return subId - A unique subscription id.
* @dev You can manage the consumer set dynamically with addConsumer/removeConsumer.
* @dev Note to fund the subscription, use transferAndCall. For example
* @dev LINKTOKEN.transferAndCall(
* @dev address(COORDINATOR),
* @dev amount,
* @dev abi.encode(subId));
*/
function createSubscription() external returns (uint64 subId);
/**
* @notice Get a VRF subscription.
* @param subId - ID of the subscription
* @return balance - LINK balance of the subscription in juels.
* @return reqCount - number of requests for this subscription, determines fee tier.
* @return owner - owner of the subscription.
* @return consumers - list of consumer address which are able to use this subscription.
*/
function getSubscription(uint64 subId)
external
view
returns (
uint96 balance,
uint64 reqCount,
address owner,
address[] memory consumers
);
/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @param newOwner - proposed new owner of the subscription
*/
function requestSubscriptionOwnerTransfer(uint64 subId, address newOwner) external;
/**
* @notice Request subscription owner transfer.
* @param subId - ID of the subscription
* @dev will revert if original owner of subId has
* not requested that msg.sender become the new owner.
*/
function acceptSubscriptionOwnerTransfer(uint64 subId) external;
/**
* @notice Add a consumer to a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - New consumer which can use the subscription
*/
function addConsumer(uint64 subId, address consumer) external;
/**
* @notice Remove a consumer from a VRF subscription.
* @param subId - ID of the subscription
* @param consumer - Consumer to remove from the subscription
*/
function removeConsumer(uint64 subId, address consumer) external;
/**
* @notice Cancel a subscription
* @param subId - ID of the subscription
* @param to - Where to send the remaining LINK to
*/
function cancelSubscription(uint64 subId, address to) external;
/*
* @notice Check to see if there exists a request commitment consumers
* for all consumers and keyhashes for a given sub.
* @param subId - ID of the subscription
* @return true if there exists at least one unfulfilled request for the subscription, false
* otherwise.
*/
function pendingRequestExists(uint64 subId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/** ****************************************************************************
* @notice Interface for contracts using VRF randomness
* *****************************************************************************
* @dev PURPOSE
*
* @dev Reggie the Random Oracle (not his real job) wants to provide randomness
* @dev to Vera the verifier in such a way that Vera can be sure he's not
* @dev making his output up to suit himself. Reggie provides Vera a public key
* @dev to which he knows the secret key. Each time Vera provides a seed to
* @dev Reggie, he gives back a value which is computed completely
* @dev deterministically from the seed and the secret key.
*
* @dev Reggie provides a proof by which Vera can verify that the output was
* @dev correctly computed once Reggie tells it to her, but without that proof,
* @dev the output is indistinguishable to her from a uniform random sample
* @dev from the output space.
*
* @dev The purpose of this contract is to make it easy for unrelated contracts
* @dev to talk to Vera the verifier about the work Reggie is doing, to provide
* @dev simple access to a verifiable source of randomness. It ensures 2 things:
* @dev 1. The fulfillment came from the VRFCoordinator
* @dev 2. The consumer contract implements fulfillRandomWords.
* *****************************************************************************
* @dev USAGE
*
* @dev Calling contracts must inherit from VRFConsumerBase, and can
* @dev initialize VRFConsumerBase's attributes in their constructor as
* @dev shown:
*
* @dev contract VRFConsumer {
* @dev constructor(<other arguments>, address _vrfCoordinator, address _link)
* @dev VRFConsumerBase(_vrfCoordinator) public {
* @dev <initialization with other arguments goes here>
* @dev }
* @dev }
*
* @dev The oracle will have given you an ID for the VRF keypair they have
* @dev committed to (let's call it keyHash). Create subscription, fund it
* @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface
* @dev subscription management functions).
* @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations,
* @dev callbackGasLimit, numWords),
* @dev see (VRFCoordinatorInterface for a description of the arguments).
*
* @dev Once the VRFCoordinator has received and validated the oracle's response
* @dev to your request, it will call your contract's fulfillRandomWords method.
*
* @dev The randomness argument to fulfillRandomWords is a set of random words
* @dev generated from your requestId and the blockHash of the request.
*
* @dev If your contract could have concurrent requests open, you can use the
* @dev requestId returned from requestRandomWords to track which response is associated
* @dev with which randomness request.
* @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind,
* @dev if your contract could have multiple requests in flight simultaneously.
*
* @dev Colliding `requestId`s are cryptographically impossible as long as seeds
* @dev differ.
*
* *****************************************************************************
* @dev SECURITY CONSIDERATIONS
*
* @dev A method with the ability to call your fulfillRandomness method directly
* @dev could spoof a VRF response with any random value, so it's critical that
* @dev it cannot be directly called by anything other than this base contract
* @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method).
*
* @dev For your users to trust that your contract's random behavior is free
* @dev from malicious interference, it's best if you can write it so that all
* @dev behaviors implied by a VRF response are executed *during* your
* @dev fulfillRandomness method. If your contract must store the response (or
* @dev anything derived from it) and use it later, you must ensure that any
* @dev user-significant behavior which depends on that stored value cannot be
* @dev manipulated by a subsequent VRF request.
*
* @dev Similarly, both miners and the VRF oracle itself have some influence
* @dev over the order in which VRF responses appear on the blockchain, so if
* @dev your contract could have multiple VRF requests in flight simultaneously,
* @dev you must ensure that the order in which the VRF responses arrive cannot
* @dev be used to manipulate your contract's user-significant behavior.
*
* @dev Since the block hash of the block which contains the requestRandomness
* @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
* @dev miner could, in principle, fork the blockchain to evict the block
* @dev containing the request, forcing the request to be included in a
* @dev different block with a different hash, and therefore a different input
* @dev to the VRF. However, such an attack would incur a substantial economic
* @dev cost. This cost scales with the number of blocks the VRF oracle waits
* @dev until it calls responds to a request. It is for this reason that
* @dev that you can signal to an oracle you'd like them to wait longer before
* @dev responding to the request (however this is not enforced in the contract
* @dev and so remains effective only in the case of unmodified oracle software).
*/
abstract contract VRFConsumerBaseV2 {
error OnlyCoordinatorCanFulfill(address have, address want);
address private immutable vrfCoordinator;
/**
* @param _vrfCoordinator address of VRFCoordinator contract
*/
constructor(address _vrfCoordinator) {
vrfCoordinator = _vrfCoordinator;
}
/**
* @notice fulfillRandomness handles the VRF response. Your contract must
* @notice implement it. See "SECURITY CONSIDERATIONS" above for important
* @notice principles to keep in mind when implementing your fulfillRandomness
* @notice method.
*
* @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this
* @dev signature, and will call it once it has verified the proof
* @dev associated with the randomness. (It is triggered via a call to
* @dev rawFulfillRandomness, below.)
*
* @param requestId The Id initially returned by requestRandomness
* @param randomWords the VRF output expanded to the requested number of words
*/
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;
// rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
// proof. rawFulfillRandomness then calls fulfillRandomness, after validating
// the origin of the call
function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
if (msg.sender != vrfCoordinator) {
revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
}
fulfillRandomWords(requestId, randomWords);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @dev Collection of functions related to array types.
* Modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Arrays.sol
*/
library Arrays {
/**
* @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).
*
* `array` is expected to be sorted in ascending order, and to contain no
* repeated elements.
*/
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);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds down (it does integer division with truncation).
if (array[mid] > element) {
high = mid;
} else {
unchecked {
low = mid + 1;
}
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && array[low - 1] == element) {
unchecked {
return low - 1;
}
} else {
return low;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
/**
* @title WinningEntrySearchLogicV2
* @notice This contract contains the logic to search for a winning entry.
* @author LooksRare protocol team (👀,💎)
*/
contract WinningEntrySearchLogicV2 {
/**
* @param randomWord The random word.
* @param currentEntryIndex The current entry index.
* @param winningEntriesBitmap The bitmap of winning entries.
*/
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);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface IRaffleV2 {
enum RaffleStatus {
None,
Open,
Drawing,
RandomnessFulfilled,
Drawn,
Complete,
Refundable,
Cancelled
}
enum TokenType {
ERC721,
ERC1155,
ETH,
ERC20
}
/**
* @param entriesCount The number of entries that can be purchased for the given price.
* @param price The price of the entries.
*/
struct PricingOption {
uint40 entriesCount;
uint208 price;
}
/**
* @param currentEntryIndex The cumulative number of entries in the raffle minus one.
* @param participant The address of the participant.
*/
struct Entry {
uint40 currentEntryIndex;
address participant;
}
/**
* @param participant The address of the winner.
* @param claimed Whether the winner has claimed the prize.
* @param prizeIndex The index of the prize that was won.
* @param entryIndex The index of the entry that won.
*/
struct Winner {
address participant;
bool claimed;
uint8 prizeIndex;
uint40 entryIndex;
}
/**
* @param winnersCount The number of winners.
* @param cumulativeWinnersCount The cumulative number of winners in the raffle.
* @param prizeType The type of the prize.
* @param prizeTier The tier of the prize.
* @param prizeAddress The address of the prize.
* @param prizeId The id of the prize.
* @param prizeAmount The amount of the prize.
*/
struct Prize {
uint40 winnersCount;
uint40 cumulativeWinnersCount;
TokenType prizeType;
uint8 prizeTier;
address prizeAddress;
uint256 prizeId;
uint256 prizeAmount;
}
/**
* @param owner The address of the raffle owner.
* @param status The status of the raffle.
* @param isMinimumEntriesFixed Whether the minimum number of entries is fixed.
* @param cutoffTime The time after which the raffle cannot be entered.
* @param drawnAt The time at which the raffle was drawn. It is still pending Chainlink to fulfill the randomness request.
* @param minimumEntries The minimum number of entries required to draw the raffle.
* @param maximumEntriesPerParticipant The maximum number of entries allowed per participant.
* @param feeTokenAddress The address of the token to be used as a fee. If the fee token type is ETH, then this address is ignored.
* @param protocolFeeBp The protocol fee in basis points. It must be equal to the protocol fee basis points when the raffle was created.
* @param claimableFees The amount of fees collected from selling entries.
* @param pricingOptions The pricing options for the raffle.
* @param prizes The prizes to be distributed.
* @param entries The entries that have been sold.
* @param winners The winners of the raffle.
*/
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;
}
/**
* @param amountPaid The amount paid by the participant.
* @param entriesCount The number of entries purchased by the participant.
* @param refunded Whether the participant has been refunded.
*/
struct ParticipantStats {
uint208 amountPaid;
uint40 entriesCount;
bool refunded;
}
/**
* @param raffleId The id of the raffle.
* @param pricingOptionIndex The index of the selected pricing option.
* @param count The number of entries to be purchased.
* @param recipient The recipient of the entries.
*/
struct EntryCalldata {
uint256 raffleId;
uint256 pricingOptionIndex;
uint40 count;
address recipient;
}
/**
* @param cutoffTime The time at which the raffle will be closed.
* @param minimumEntries The minimum number of entries required to draw the raffle.
* @param isMinimumEntriesFixed Whether the minimum number of entries is fixed.
* @param maximumEntriesPerParticipant The maximum number of entries allowed per participant.
* @param protocolFeeBp The protocol fee in basis points. It must be equal to the protocol fee basis points when the raffle was created.
* @param feeTokenAddress The address of the token to be used as a fee. If the fee token type is ETH, then this address is ignored.
* @param prizes The prizes to be distributed.
* @param pricingOptions The pricing options for the raffle.
*/
struct CreateRaffleCalldata {
uint40 cutoffTime;
bool isMinimumEntriesFixed;
uint40 minimumEntries;
uint40 maximumEntriesPerParticipant;
uint16 protocolFeeBp;
address feeTokenAddress;
Prize[] prizes;
PricingOption[] pricingOptions;
}
/**
* @param raffleId The id of the raffle.
* @param winnerIndices The indices of the winners to be claimed.
*/
struct ClaimPrizesCalldata {
uint256 raffleId;
uint256[] winnerIndices;
}
/**
* @param exists Whether the request exists.
* @param randomWord The random words returned by Chainlink VRF.
* If randomWord == 0, then the request is still pending.
* @param raffleId The id of the raffle.
*/
struct RandomnessRequest {
bool exists;
uint80 raffleId;
uint256 randomWord;
}
/**
* @notice This is used to accumulate the amount of tokens to be transferred.
* @param tokenAddress The address of the token.
* @param amount The amount of tokens accumulated.
*/
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();
/**
* @notice Creates a new raffle.
* @param params The parameters of the raffle.
* @return raffleId The id of the newly created raffle.
*/
function createRaffle(CreateRaffleCalldata calldata params) external payable returns (uint256 raffleId);
/**
* @notice Enters a raffle or multiple raffles.
* @param entries The entries to be made.
*/
function enterRaffles(EntryCalldata[] calldata entries) external payable;
/**
* @notice Select the winners for a raffle based on the random words returned by Chainlink.
* @param requestId The request id returned by Chainlink.
*/
function selectWinners(uint256 requestId) external;
/**
* @notice Claims a single prize for a winner.
* @param raffleId The id of the raffle.
* @param winnerIndex The index of the winner.
*/
function claimPrize(uint256 raffleId, uint256 winnerIndex) external;
/**
* @notice Claims the prizes for a winner. A winner can claim multiple prizes
* from multiple raffles in a single transaction.
* @param claimPrizesCalldata The calldata for claiming prizes.
*/
function claimPrizes(ClaimPrizesCalldata[] calldata claimPrizesCalldata) external;
/**
* @notice Claims the fees collected for a raffle.
* @param raffleId The id of the raffle.
*/
function claimFees(uint256 raffleId) external;
/**
* @notice Cancels a raffle beyond cut-off time without meeting minimum entries.
* @param raffleId The id of the raffle.
*/
function cancel(uint256 raffleId) external;
/**
* @notice Draws winners for a raffle beyond cut-off time without meeting minimum entries.
* @param raffleId The id of the raffle.
*/
function drawWinners(uint256 raffleId) external;
/**
* @notice Cancels a raffle after randomness request if the randomness request
* does not arrive after a certain amount of time.
* Only callable by contract owner.
* @param raffleId The id of the raffle.
*/
function cancelAfterRandomnessRequest(uint256 raffleId) external;
/**
* @notice Withdraws the prizes for a raffle after it has been marked as refundable.
* @param raffleId The id of the raffle.
*/
function withdrawPrizes(uint256 raffleId) external;
/**
* @notice Rollover entries from cancelled raffles to open raffles.
* @param refundableRaffleIds The ids of the refundable raffles.
* @param entries The entries to be made.
*/
function rollover(uint256[] calldata refundableRaffleIds, EntryCalldata[] calldata entries) external payable;
/**
* @notice Claims the refund for a cancelled raffle.
* @param raffleIds The ids of the raffles.
*/
function claimRefund(uint256[] calldata raffleIds) external;
/**
* @notice Sets the protocol fee in basis points. Only callable by contract owner.
* @param protocolFeeBp The protocol fee in basis points.
*/
function setProtocolFeeBp(uint16 protocolFeeBp) external;
/**
* @notice Sets the protocol fee recipient. Only callable by contract owner.
* @param protocolFeeRecipient The protocol fee recipient.
*/
function setProtocolFeeRecipient(address protocolFeeRecipient) external;
/**
* @notice This function allows the owner to update currency statuses.
* @param currencies Currency addresses (address(0) for ETH)
* @param isAllowed Whether the currencies should be allowed for trading
* @dev Only callable by owner.
*/
function updateCurrenciesStatus(address[] calldata currencies, bool isAllowed) external;
/**
* @notice Toggle the contract's paused status. Only callable by contract owner.
*/
function togglePaused() external;
/**
* @notice Gets the winners for a raffle.
* @param raffleId The id of the raffle.
* @return winners The winners of the raffle.
*/
function getWinners(uint256 raffleId) external view returns (Winner[] memory);
/**
* @notice Gets the pricing options for a raffle.
* @param raffleId The id of the raffle.
* @return pricingOptions The pricing options for the raffle.
*/
function getPricingOptions(uint256 raffleId) external view returns (PricingOption[] memory);
/**
* @notice Gets the prizes for a raffle.
* @param raffleId The id of the raffle.
* @return prizes The prizes to be distributed.
*/
function getPrizes(uint256 raffleId) external view returns (Prize[] memory);
/**
* @notice Gets the entries for a raffle.
* @param raffleId The id of the raffle.
* @return entries The entries entered for the raffle.
*/
function getEntries(uint256 raffleId) external view returns (Entry[] memory);
}// SPDX-License-Identifier: MIT
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;
}// SPDX-License-Identifier: MIT
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);
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @notice It is emitted if the ETH transfer fails. */ error ETHTransferFail(); /** * @notice It is emitted if the ERC20 approval fails. */ error ERC20ApprovalFail(); /** * @notice It is emitted if the ERC20 transfer fails. */ error ERC20TransferFail(); /** * @notice It is emitted if the ERC20 transferFrom fails. */ error ERC20TransferFromFail(); /** * @notice It is emitted if the ERC721 transferFrom fails. */ error ERC721TransferFromFail(); /** * @notice It is emitted if the ERC1155 safeTransferFrom fails. */ error ERC1155SafeTransferFromFail(); /** * @notice It is emitted if the ERC1155 safeBatchTransferFrom fails. */ error ERC1155SafeBatchTransferFromFail();
// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; /** * @notice It is emitted if the call recipient is not a contract. */ error NotAContract();
// SPDX-License-Identifier: MIT
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);
}// SPDX-License-Identifier: MIT
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;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title IOwnableTwoSteps
* @author LooksRare protocol team (👀,💎)
*/
interface IOwnableTwoSteps {
/**
* @notice This enum keeps track of the ownership status.
* @param NoOngoingTransfer The default status when the owner is set
* @param TransferInProgress The status when a transfer to a new owner is initialized
* @param RenouncementInProgress The status when a transfer to address(0) is initialized
*/
enum Status {
NoOngoingTransfer,
TransferInProgress,
RenouncementInProgress
}
/**
* @notice This is returned when there is no transfer of ownership in progress.
*/
error NoOngoingTransferInProgress();
/**
* @notice This is returned when the caller is not the owner.
*/
error NotOwner();
/**
* @notice This is returned when there is no renouncement in progress but
* the owner tries to validate the ownership renouncement.
*/
error RenouncementNotInProgress();
/**
* @notice This is returned when the transfer is already in progress but the owner tries
* initiate a new ownership transfer.
*/
error TransferAlreadyInProgress();
/**
* @notice This is returned when there is no ownership transfer in progress but the
* ownership change tries to be approved.
*/
error TransferNotInProgress();
/**
* @notice This is returned when the ownership transfer is attempted to be validated by the
* a caller that is not the potential owner.
*/
error WrongPotentialOwner();
/**
* @notice This is emitted if the ownership transfer is cancelled.
*/
event CancelOwnershipTransfer();
/**
* @notice This is emitted if the ownership renouncement is initiated.
*/
event InitiateOwnershipRenouncement();
/**
* @notice This is emitted if the ownership transfer is initiated.
* @param previousOwner Previous/current owner
* @param potentialOwner Potential/future owner
*/
event InitiateOwnershipTransfer(address previousOwner, address potentialOwner);
/**
* @notice This is emitted when there is a new owner.
*/
event NewOwner(address newOwner);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title IReentrancyGuard
* @author LooksRare protocol team (👀,💎)
*/
interface IReentrancyGuard {
/**
* @notice This is returned when there is a reentrant call.
*/
error ReentrancyFail();
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
enum TokenType {
ERC20,
ERC721,
ERC1155
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a / b + (a % b == 0 ? 0 : 1);
}
}{
"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/",
"lib/forge-std:ds-test/=lib/forge-std/lib/ds-test/src/"
],
"optimizer": {
"enabled": true,
"runs": 888888
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"viaIR": true,
"libraries": {}
}Contract ABI
API[{"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"}]Contract Creation Code
61014034620002695762005cfc601f38829003908101601f19168301906001600160401b0390818311858410176200026e5780859460409485528539610100948591810103126200026957620000558362000284565b9060208401518385015191821682036200026957620000776060860162000284565b90620000866080870162000284565b936200009560a0880162000284565b60c088015161ffff811698898203620002695760e0620000b6910162000284565b6080869052600080546001600160a01b0319166001600160a01b03998a1690811790915589519081529099907f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc90602090a16001805460ff60a81b1916600160a81b1790556109c4811162000258577fede4aee4284b8033b84c1aadcc51b229a4e46e6b42ab40092e237f07508b46266020600654928b51908152a187831692831562000247576001600160501b0390911660f09290921b6001600160f01b0319169190911760509190911b600160501b600160f01b031617600655865190815285939291907fc1b5345cce283376356748dc57f2dfa7120431d016fc7ca9ba641bc65f91411d90602090a160a05260c05216855260e052610120921682525190615a6292836200029a843960805183615545015260a0518381816141750152614fe7015260c05183614349015260e051838181610316015261436f015251826143be015251818181610bb801528181610c5701528181610d1c01528181610e1b01528181610ec00152614c420152f35b8951633106a0c760e01b8152600490fd5b8851634929acd760e01b8152600490fd5b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620002695756fe6080604052600436101561001257600080fd5b60003560e01c8063030932bb146102c25780630d64d5a6146102bd5780631e9f1a82146102b85780631fe543e3146102b3578063208b3d34146102ae57806323452b9c146102a95780632a6a4eb8146102a45780632bb5a9e61461029f57806331fe74a91461029a57806336566f061461029557806339a2eb2a146102905780633b6cb153146102545780633e5675391461028b57806340e58ee51461028657806349890e15146102815780634d2c53cd1461027c578063534fff0e146102775780635a195d19146102725780635b6ac0111461026d5780635c975abb146102685780635cb6dfff146102635780635d4bc0ce1461025e57806364df049e1461025957806365d697f7146102545780636b1426a41461024f5780636b7e4e3f1461024a5780636f30d7b5146102455780637200b829146102405780637762df251461023b5780637b154140146102365780638da5cb5b1461023157806398753c461461022c578063a17be15414610227578063a72844ba14610222578063ac68a7481461021d578063c0b6f56114610218578063cc30779514610213578063d1d06b2e1461020e578063d3549ebd14610209578063e521cb9214610204578063ea9bf39d146101ff578063f23a6e61146101fa5763f54a6f83146101f557600080fd5b613546565b6134a1565b613467565b613360565b6132aa565b613149565b613032565b612e7d565b612be3565b612aec565b612a37565b6129f8565b6129a6565b61277f565b61272d565b6125be565b61223d565b612073565b611f94565b611734565b611ebe565b611df3565b611cdb565b611c97565b611bbb565b611b52565b6119e2565b61199a565b61195f565b61186c565b61176e565b61148a565b611344565b6112a8565b6111f0565b6110e9565b610fcd565b6107aa565b610700565b6104f0565b61036b565b6102d7565b60009103126102d257565b600080fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060405167ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b9181601f840112156102d25782359167ffffffffffffffff83116102d2576020808501948460071b0101116102d257565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760043567ffffffffffffffff81116102d2576103b590369060040161033a565b600260ff60015460a81c161461048b5761043a79ffffffffffffffffffffffffffffffffffffffffffffffffffff916104449361042d75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6104356156b2565b61466f565b9190911690614c19565b61048975010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b005b60046040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b9181601f840112156102d25782359167ffffffffffffffff83116102d2576020808501948460051b0101116102d257565b801515036102d257565b346102d2576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600490813567ffffffffffffffff81116102d25761054190369084016104b5565b916024359361054f856104e6565b610557615667565b60005b8481106105985750507fcb2fef16ee9f3ea43e174daf88ffcdc8beee4ad884c7570093d998d71058698793610593915193849384613b8e565b0390a1005b600190600087156105e6575060ff825b73ffffffffffffffffffffffffffffffffffffffff6105c8848a8a613aa0565b356105d281611b27565b16600052846020521684600020550161055a565b60ff906105a8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161063157604052565b6105ee565b6080810190811067ffffffffffffffff82111761063157604052565b60e0810190811067ffffffffffffffff82111761063157604052565b6040810190811067ffffffffffffffff82111761063157604052565b610100810190811067ffffffffffffffff82111761063157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761063157604052565b67ffffffffffffffff81116106315760051b60200190565b346102d25760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760243567ffffffffffffffff81116102d257366023820112156102d25780600401359061075b826106e8565b9061076960405192836106a7565b82825260209260248484019160051b830101913683116102d257602401905b82821061079b5761048984600435615542565b81358152908401908401610788565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6020813601126102d25767ffffffffffffffff600435116102d25761010090600435360301126102d257600260ff60015460a81c161461048b5761084a75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6108526156b2565b6108606004356004016135e7565b64ffffffffff81168062015180420111908115610fbe575b50610f945761088b6084600435016135f4565b600654908160f01c61ffff821603610f6a576108ab60a4600435016135fe565b9073ffffffffffffffffffffffffffffffffffffffff8216610f5c575b6108dc60c460043501600435600401613608565b80939150158015610f52575b610f285769ffffffffffffffffffff9461093a866001818816011669ffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000006006541617600655565b85600181871601166000526002602052604060002093600485016000526020600020906000906000926000906000915b838310610b0057505050600487015561098290614fa0565b64ffffffffff6109966044600435016135e7565b911664ffffffffff82168111908115610af5575b50610acb576001936109d76109c960e4600435016004356004016137f2565b90848b89818d160116613ce8565b740100000000000000000000000000000000000000006109fb602460043501613846565b610a096064600435016135e7565b9460b01b9060a81b33171717865560f01b9260501b9160281b1717179101557fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65160405180610a6c8560018187160116826020600191939293604081019481520152565b0390a1610ab475010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6040519082166001019091168152602090f35b0390f35b60046040517f804a1e02000000000000000000000000000000000000000000000000000000008152fd5b60c8915011386109aa565b610b29610b2484610b1e60c460049a979a3501600435600401613608565b9061368b565b6136ae565b9560ff610b3a606089015160ff1690565b921660ff831610610efe57610b4e87613fb0565b604087015196610b5d88612f7e565b805164ffffffffff1694610b88608083015173ffffffffffffffffffffffffffffffffffffffff1690565b60c060a084015193015192610b9c8b612f7e565b8a610ce25773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163b156102d2576040517fcda20f0b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152336024820152306044820152606481018290529560008780608481015b03818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1968715610cdd5789600295610c979260019a610cc4575b505b6137d9565b9b8c829a600389028b019560601b9360581b9260501b9160281b171717178255858201550155019161096a565b80610cd1610cd79261061d565b806102c7565b38610c90565b613785565b610ceb8b612f7e565b60038b03610da557610d0464ffffffffff891685613772565b9573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163b156102d2576040517fda3e8ce400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015233602482015230604482015260648101979097526000878060848101610c3d565b9198610db58b9692949596612f7e565b60028b03610deb5791600195949391610c9789610de5600296610ddf64ffffffffff84168a613772565b9061379f565b9c6137d9565b9891610e0364ffffffffff8997969593971685613772565b9573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163b156102d2576040517fe62edc3500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201523360248201523060448201526064810183905260848101979097526000878060a4810103818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1968715610cdd5789600295610c979260019a610cc457506137d9565b60046040517f5f12e2ee000000000000000000000000000000000000000000000000000000008152fd5b60046040517fab13062d000000000000000000000000000000000000000000000000000000008152fd5b5060c883116108e8565b610f6582614f73565b6108c8565b60046040517f4929acd7000000000000000000000000000000000000000000000000000000008152fd5b60046040517fce8f4bfd000000000000000000000000000000000000000000000000000000008152fd5b905062093a8042011038610878565b346102d2576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110da57611005615667565b60015460ff8160a01c1660038110156110d55780156110ab578061102a6001926111e6565b14611080575b507fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff600154166001557f8eca980489e87f7dba4f26917aa4bfc906eb3f2b4f7b4b9fd0ff2b8bb3e21ae38180a180f35b7fffffffffffffffffffffffff00000000000000000000000000000000000000001660015538611030565b60046040517fccf69db7000000000000000000000000000000000000000000000000000000008152fd5b6111b7565b80fd5b61ffff8116036102d257565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435611124816110dd565b61112c615667565b61ffff8116906109c48211610f6a577fede4aee4284b8033b84c1aadcc51b229a4e46e6b42ab40092e237f07508b4626916020917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffff0000000000000000000000000000000000000000000000000000000000006006549260f01b16911617600655604051908152a1005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600311156110d557565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760ff60015460a01c1660405160038210156110d5576020918152f35b60208082019080835283518092528060408094019401926000905b83821061126557505050505090565b8451805164ffffffffff16875283015179ffffffffffffffffffffffffffffffffffffffffffffffffffff16868401529485019493820193600190910190611256565b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760043560005260028152600360406000200180546112f5816106e8565b9161130360405193846106a7565b8183526000908152838120938084015b8383106113285760405180610ac7878261123b565b600182819261133689613bfc565b815201960192019194611313565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25761137b615667565b60015460ff8160b01c16600014611417575060015460ff8160b01c16156113ed577fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff166001557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60046040517f6cd60201000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff760100000000000000000000000000000000000000000000916114596156b2565b16176001557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576114d86114d36004356000526005602052604060002090565b613850565b6114e96114e58251151590565b1590565b61170a576115156115068383015169ffffffffffffffffffff1690565b69ffffffffffffffffffff1690565b9161152a836000526002602052604060002090565b9261153484614555565b61153e8185615052565b6004840191825493611583600561157c61157261156361155d8a613899565b896138c6565b505460281c64ffffffffff1690565b64ffffffffff1690565b97016138e2565b80519361158f8561396b565b9460005b8181106116da57506115a76115ad91613899565b866139c7565b51916115c36115be8460081c613791565b61396b565b976115cd8161396b565b9760005b8281106116b657505050604080940151916000526002855260068460002001978860005285600020936000935b8b851061160a578b8b55005b89946116346116508a6116228d978761162e976153e1565b908095929791996154a0565b896139c7565b51015173ffffffffffffffffffffffffffffffffffffffff1690565b61165e6001840180996154a0565b60a81b179060b01b179087015586516116ae816116828b8201948560209181520190565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826106a7565b5190206115fe565b806116c9611572611563600194866138c6565b6116d3828d6139c7565b52016115d1565b806116f96115726116ed600194886139c7565b515164ffffffffff1690565b611703828a6139c7565b5201611593565b60046040517f1b71a84c000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060405160c88152f35b346102d2576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110da576117a6615667565b60ff60015460a01c1660038110156110d557600203611842577fffffffffffffffffffffffff000000000000000000000000000000000000000081541681556118127fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b604051600081527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc90602090a180f35b60046040517f045c5122000000000000000000000000000000000000000000000000000000008152fd5b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600260ff60015460a81c161461048b57610444906118f775020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6118ff6156b2565b80600052600260205260406000206119168161500b565b805464ffffffffff61192c818360b01c166137ac565b16421061193a575b506150c2565b73ffffffffffffffffffffffffffffffffffffffff6119599116614f2b565b38611934565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760206040516109c48152f35b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602069ffffffffffffffffffff60065416604051908152f35b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600190600260ff835460a81c161461048b57611a6b75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b611a736156b2565b806000526002602052611a94604060002091611a8e83614598565b82615132565b6004810173ffffffffffffffffffffffffffffffffffffffff815492541660005b838110611b015761048975010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b80611b21611b108793866138c6565b508464ffffffffff825416916140de565b01611ab5565b73ffffffffffffffffffffffffffffffffffffffff8116036102d257565b3590611b5082611b27565b565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25773ffffffffffffffffffffffffffffffffffffffff600435611ba281611b27565b1660005260046020526020604060002054604051908152f35b346102d2576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110da57611bf3615667565b60015460ff8160a01c1660038110156110d557611c6d577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674020000000000000000000000000000000000000000176001557f3ff05a45e46337fa1cbf20996d2eeb927280bce099f37252bcca1040609604ec8180a180f35b60046040517f74ed79ae000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060ff60015460b01c166040519015158152f35b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576004356000526005602052606060406000206001815491015469ffffffffffffffffffff6040519260ff81161515845260081c1660208301526040820152f35b600811156110d557565b9893969a999597919a9290926101408a019b73ffffffffffffffffffffffffffffffffffffffff8095168b5260088110156110d5576101209979ffffffffffffffffffffffffffffffffffffffffffffffffffff9961ffff988d9560609460208801521515604087015264ffffffffff958694858094169101521660808d01521660a08b01521660c08901521660e08701521661010085015216910152565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600052600260205260406000208054610ac7611e636002600185015494015479ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b60405193849373ffffffffffffffffffffffffffffffffffffffff9164ffffffffff928160f01c93818360501c169381808560281c169416928160d81c928260b01c169160ff8160a81c169160ff8260a01c1691168b611d54565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602073ffffffffffffffffffffffffffffffffffffffff60065460501c16604051908152f35b60208082019080835283518092528060408094019401926000905b838210611f3d57505050505090565b8451805173ffffffffffffffffffffffffffffffffffffffff168752808401511515878501528082015160ff168783015260609081015164ffffffffff169087015260809095019493820193600190910190611f2e565b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576004356000526002815260409060068260002001805490611fe4826106e8565b92611ff1855194856106a7565b82845260009182528082208185015b84841061201457865180610ac78882611f13565b6001838192895161202481610636565b64ffffffffff865473ffffffffffffffffffffffffffffffffffffffff8116835260ff808260a01c161515868501528160a81c168d84015260b01c166060820152815201920193019290612000565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25767ffffffffffffffff6004358181116102d2576120be9036906004016104b5565b916024359081116102d2576120d790369060040161033a565b929091600260ff60015460a81c161461048b5761214c916121449161213775020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b61213f6156b2565b614ce4565b93909261466f565b9073ffffffffffffffffffffffffffffffffffffffff809116908316036121c65779ffffffffffffffffffffffffffffffffffffffffffffffffffff9081169216828111156121a35791610444920390339061414d565b8281106121b3575b505050610444565b6121be920390614c19565b3880806121ab565b60046040517ff5993428000000000000000000000000000000000000000000000000000000008152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126102d2576004359067ffffffffffffffff82116102d257612239916004016104b5565b9091565b346102d25761224b366121f0565b90600260ff60015460a81c161461048b576122a175020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6122a96156b2565b6122b16139f3565b9160005b81811061231a5783602081015161230b5761048975010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b61231490614290565b80610444565b612325818385613a0c565b61233a81356000526002602052604060002090565b90600461234c835460ff9060a01c1690565b61235581611d4a565b036125b0575b6123686020820182613a4c565b9092600681015460005b8381106123bc57505050917f0d0ab4a45afc0276ae825be9eeb4552011b19aad5c92b4bb782f3ca0356fedc5916123b3600195946040519384933584613ab0565b0390a1016122b5565b6123c7818588613aa0565b3582811015612586576123dd90600685016139db565b509081549160ff8360a01c1661255c5760019261246861245f8e9361241873ffffffffffffffffffffffffffffffffffffffff809516614f2b565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001781555460a81c60ff1690565b600488016138c6565b50908461247a835460ff9060501c1690565b61248381612f7e565b111561254a57815460601c906124c96124b0855173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b908216036124f057506002915001546124e760208d0191825161379f565b90525b01612372565b9061251d60029260208501948551612526579073ffffffffffffffffffffffffffffffffffffffff169052565b015490526124ea565b61252f81614290565b9073ffffffffffffffffffffffffffffffffffffffff169052565b506125579150339061405e565b6124ea565b60046040517f969bf728000000000000000000000000000000000000000000000000000000008152fd5b60046040517f63df8171000000000000000000000000000000000000000000000000000000008152fd5b6125b9826145b1565b61235b565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600154600160ff8260a01c16612602816111e6565b036127035773ffffffffffffffffffffffffffffffffffffffff1633036126d957600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000163317905561267a7fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b6126a77fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b6040513381527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc908060208101610593565b60046040517fafdcfb92000000000000000000000000000000000000000000000000000000008152fd5b60046040517f5e4f2826000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346102d25760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576004803560243590600260ff60015460a81c161461297d5761280b75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6128136156b2565b612827816000526002602052604060002090565b8054849060a01c60ff1661283a81611d4a565b0361296f575b6006810180548410156129465783612857916139db565b50805460ff8160a01c1661291d57917fd53b67ba94a5d6268d11caa5d2693557779404ed02fc9825d86d2894d29cb8fd956128b16128b89361241873ffffffffffffffffffffffffffffffffffffffff6128c09716614f2b565b91016138c6565b50339061405e565b60408051918252602082019290925290819081015b0390a161048975010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b856040517f969bf728000000000000000000000000000000000000000000000000000000008152fd5b846040517f63df8171000000000000000000000000000000000000000000000000000000008152fd5b612978816145b1565b612840565b826040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060065460f01c604051908152f35b346102d25760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576060612aac602435612a7781611b27565b600435600052600360205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b546040519079ffffffffffffffffffffffffffffffffffffffffffffffffffff8116825264ffffffffff8160d01c16602083015260f81c15156040820152f35b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600260ff60015460a81c161461048b57612b7375020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b612b7b6156b2565b806000526002602052604060002090612b93826145ca565b815460d81c620151808101809111612bde574210612bb457610444916150c2565b60046040517ff4c0ca6e000000000000000000000000000000000000000000000000000000008152fd5b613732565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600260ff60015460a81c161461048b577fa1f87f32d0f17fab0242ca800d736293de8988c14b27747e218cf13d5c249f5390612c8c75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b612c946156b2565b80612ca9816000526002602052604060002090565b612cb2816145e3565b612d9a612cd3825473ffffffffffffffffffffffffffffffffffffffff1690565b9173ffffffffffffffffffffffffffffffffffffffff83163303612e51575b612de66002820191612ddf612d21845479ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b93612dc26001840191612d6f612d4b612d45612d3e865460f01c90565b61ffff1690565b89613af8565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff61271091160490565b97889779ffffffffffffffffffffffffffffffffffffffffffffffffffff998a9103169b8c966151a2565b7fffffffffffff00000000000000000000000000000000000000000000000000008154169055565b5460501c73ffffffffffffffffffffffffffffffffffffffff1690565b948561414d565b1680612e23575b50506040805191825279ffffffffffffffffffffffffffffffffffffffffffffffffffff909216602082015290819081016128d5565b600654612e4a929060501c73ffffffffffffffffffffffffffffffffffffffff169061414d565b3880612ded565b612e78612e7360005473ffffffffffffffffffffffffffffffffffffffff1690565b614f2b565b612cf2565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435612eb881611b27565b612ec0615667565b60015460ff8160a01c1660038110156110d557611c6d577fffffffffffffffffffffff0000000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff82161774010000000000000000000000000000000000000000176001557fb86c75c9bffca616b2d314cc914f7c3f1d174255b16b941c3f3ededee276d5ef90610593906040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015290918291820190565b600411156110d557565b60208082019080835283518092528060408094019401926000905b838210612fb257505050505090565b9091929394855164ffffffffff8082511683528582015116858301528281015160048110156110d5578284015260608181015160ff169083015260808082015173ffffffffffffffffffffffffffffffffffffffff169083015260a0808201519083015260c0908101519082015260e00194830193929160010190612fa3565b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600052600280825260409160048360002001805491613083836106e8565b93613090865195866106a7565b8385526000928352818320908286015b8585106130b457875180610ac78982612f88565b6003846001928a516130c581610652565b613129875464ffffffffff80821684528160281c16858401528d6130f360ff91828460501c16908601613bf0565b6131096060918360581c168286019060ff169052565b1c608083019073ffffffffffffffffffffffffffffffffffffffff169052565b8487015460a08201528587015460c08201528152019301940193916130a0565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600260ff60015460a81c161461048b576131d075020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6131d86156b2565b6131ec816000526002602052604060002090565b6005810180549081156132805761324661157261324161323564ffffffffff9461322f61322961156360048b016132238154613899565b906138c6565b97613899565b906139db565b505464ffffffffff1690565b6137c3565b911611613280576104449161325a8261500b565b61327b612e73835473ffffffffffffffffffffffffffffffffffffffff1690565b6142c8565b60046040517f9358a89d000000000000000000000000000000000000000000000000000000008152fd5b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600052600281526040906132f6600583600020016138e2565b8251928284938401908085528351809252808386019401926000905b83821061331f5786860387f35b8451805164ffffffffff16875283015173ffffffffffffffffffffffffffffffffffffffff1686840152879650948501949382019360019190910190613312565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760043561339b81611b27565b6133a3615667565b73ffffffffffffffffffffffffffffffffffffffff811690811561343d577fc1b5345cce283376356748dc57f2dfa7120431d016fc7ca9ba641bc65f91411d916020917fffff0000000000000000000000000000000000000000ffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffff000000000000000000006006549260501b16911617600655604051908152a1005b60046040517f3106a0c7000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060405160058152f35b346102d25760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576134db600435611b27565b6134e6602435611b27565b60843567ffffffffffffffff8082116102d257366023830112156102d25781600401359081116102d257369101602401116102d2576040517ff23a6e61000000000000000000000000000000000000000000000000000000008152602090f35b346102d257613554366121f0565b600260ff60015460a81c161461048b576135cc79ffffffffffffffffffffffffffffffffffffffffffffffffffff916104449361213775020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b9190911690339061414d565b64ffffffffff8116036102d257565b356135f1816135d8565b90565b356135f1816110dd565b356135f181611b27565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156102d2570180359067ffffffffffffffff82116102d2576020019160e08202360383136102d257565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919081101561369b5760e0020190565b61365c565b359060ff821682036102d257565b60e0813603126102d257604051906136c582610652565b80356136d0816135d8565b825260208101356136e0816135d8565b602083015260408101359060048210156102d25760c0916040840152613708606082016136a0565b606084015261371960808201611b45565b608084015260a081013560a0840152013560c082015290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9081156001838004141715612bde57565b81810292918115918404141715612bde57565b6040513d6000823e3d90fd5b9060018201809211612bde57565b91908201809211612bde57565b90610e1064ffffffffff80931601918211612bde57565b90600164ffffffffff80931601918211612bde57565b91909164ffffffffff80809416911601918211612bde57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156102d2570180359067ffffffffffffffff82116102d257602001918160061b360383136102d257565b356135f1816104e6565b906040516060810181811067ffffffffffffffff8211176106315760405260406001829469ffffffffffffffffffff815460ff81161515865260081c1660208501520154910152565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211612bde57565b805482101561369b576000526003602060002091020190600090565b9081546138ee816106e8565b926040936138fe855191826106a7565b828152809460208092019260005281600020906000935b85851061392457505050505050565b600184819284516139348161066e565b73ffffffffffffffffffffffffffffffffffffffff875464ffffffffff8116835260281c1683820152815201930194019391613915565b90613975826106e8565b61398260405191826106a7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06139b082946106e8565b0190602036910137565b80511561369b5760200190565b805182101561369b5760209160051b010190565b805482101561369b5760005260206000200190600090565b60405190613a008261066e565b60006020838281520152565b919081101561369b5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1813603018212156102d2570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156102d2570180359067ffffffffffffffff82116102d257602001918160051b360383136102d257565b919081101561369b5760051b0190565b908152604060208201528260408201527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116102d25760609260051b809284830137010190565b919079ffffffffffffffffffffffffffffffffffffffffffffffffffff8080941691169283820216928184041490151715612bde57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b9079ffffffffffffffffffffffffffffffffffffffffffffffffffff809116918215613b8957160490565b613b2f565b6040808252810183905292939291606083019060005b818110613bb957505060209150931515910152565b909160019073ffffffffffffffffffffffffffffffffffffffff8535613bde81611b27565b16815260209485019401929101613ba4565b60048210156110d55752565b90604051613c098161066e565b915464ffffffffff8116835260281c79ffffffffffffffffffffffffffffffffffffffffffffffffffff166020830152565b901561369b5790565b919081101561369b5760061b0190565b6040813603126102d257602060405191613c6d8361066e565b8035613c78816135d8565b8352013579ffffffffffffffffffffffffffffffffffffffffffffffffffff811681036102d257602082015290565b9064ffffffffff809116918215613b8957160690565b9079ffffffffffffffffffffffffffffffffffffffffffffffffffff809116918215613b8957160690565b83158015613fa6575b613f7c57613d07613d028585613c3b565b6135e7565b90600052602091600283526040600381600020019485600052846000209260005b888110613d39575050505050505055565b613d4c613d47828b86613c44565b613c54565b613d8188613d5f835164ffffffffff1690565b92015179ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b82613e055764ffffffffff613d968386613ca7565b1615801590613de1575b613db85790600192915b60281b178187015501613d28565b600486517f0b9752e0000000000000000000000000000000000000000000000000000000008152fd5b5079ffffffffffffffffffffffffffffffffffffffffffffffffffff811615613da0565b613e35613d477fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85018d88613c44565b613e6a613e5e8b83015179ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b915164ffffffffff1690565b64ffffffffff9182613e7c8c87613ca7565b161592831593613f4a575b8315613f3b575b8315613f11575b8315613ead575b505050613db8579060019291613daa565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff929350613f069181613edf613ee793891688613b5e565b951690613b5e565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b911611388080613e9c565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff82811690861611159350613e95565b82811686821611159350613e8e565b925079ffffffffffffffffffffffffffffffffffffffffffffffffffff613f7384871686613cbd565b16151592613e87565b60046040517f3116ff5d000000000000000000000000000000000000000000000000000000008152fd5b5060058411613cf1565b6040810151613fbe81612f7e565b613fc781612f7e565b80613ff95750600160c08201511490811591613fe5575b50610efe57565b5164ffffffffff1660011415905038613fde565b80614005600392612f7e565b14614030575b60c08101511590811561401f5750610efe57565b5164ffffffffff1615905038613fde565b614059614054608083015173ffffffffffffffffffffffffffffffffffffffff1690565b614f73565b61400b565b9081549060ff8260501c169160601c9161407781612f7e565b8061408d57506001611b50930154913090615821565b8061409c600192959495612f7e565b036140c15790611b5092916140b960026001840154930154613761565b923090615749565b90600201549081800460011482151715612bde57611b509261414d565b80549192606083901c9260501c60ff166140f781612f7e565b8061410f575050916001611b50930154913090615821565b8061411e600192969396612f7e565b03614139576140b9611b509460026001850154940154613772565b90611b5093600261414b920154613772565b915b90919073ffffffffffffffffffffffffffffffffffffffff8116614199575090611b50915a917f00000000000000000000000000000000000000000000000000000000000000006158e3565b803b15614266576040517fa9059cbb000000000000000000000000000000000000000000000000000000006020820190815273ffffffffffffffffffffffffffffffffffffffff90941660248201526044810192909252600092839283906142048160648101611682565b51925af16142106156eb565b901561423c57805180614221575050565b816020806114e593614236950101910161580c565b61423c57565b60046040517ff1568f95000000000000000000000000000000000000000000000000000000008152fd5b60046040517f09ee12d5000000000000000000000000000000000000000000000000000000008152fd5b611b5090602073ffffffffffffffffffffffffffffffffffffffff82511691015190339061414d565b908160209103126102d2575190565b90614321906142d78382615212565b80547affffffffffffffffffffffffffffffffffffffffffffffffffffff164260d81b7fffffffffff00000000000000000000000000000000000000000000000000000016179055565b6040517f5d3b1d300000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201527f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff166024820152600360448201526207a12060648201526001608482015260208160a48160007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165af1908115610cdd57600091614527575b5061442361441c826000526005602052604060002090565b5460ff1690565b6144fd577f3d94fecedaa4f90b8bd459797adb95f5bb11426025c5541390d9ccc1ad1b60a19161448b614460836000526005602052604060002090565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b6144eb69ffffffffffffffffffff82166144af846000526005602052604060002090565b907fffffffffffffffffffffffffffffffffffffffffff00000000000000000000ff6affffffffffffffffffff0083549260081b169116179055565b604080519182526020820192909252a1565b60046040517ff9012132000000000000000000000000000000000000000000000000000000008152fd5b614548915060203d811161454e575b61454081836106a7565b8101906142b9565b38614404565b503d614536565b5460a01c60ff1660088110156110d55760030361456e57565b60046040517ff525e320000000000000000000000000000000000000000000000000000000008152fd5b5460a01c60ff1660088110156110d55760060361456e57565b5460a01c60ff1660088110156110d55760050361456e57565b5460a01c60ff1660088110156110d55760020361456e57565b5460a01c60ff1660088110156110d55760040361456e57565b5460a01c60ff1660088110156110d55760010361456e57565b919081101561369b5760071b0190565b91909164ffffffffff80809416911602918216918203612bde57565b91909179ffffffffffffffffffffffffffffffffffffffffffffffffffff80809416911601918211612bde57565b90916000926000928380925b8084106146885750505050565b9091929594614698878386614615565b96606088016146a96124b0826135fe565b614c0a575033965b8835906146c8826000526002602052604060002090565b9083614b9a5750600181015460501c73ffffffffffffffffffffffffffffffffffffffff16995b6020808201356003840180548210156125865761470b856145fc565b84546147209060b01c64ffffffffff16611572565b421015614b705761473a91614734916139db565b50613bfc565b9a61474860408094016135e7565b64ffffffffff92838216918215614b47576147a192918f61479c9261477561477a925164ffffffffff1690565b614625565b9f015179ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b613af8565b906147f48d610c926147e6846147c18b6000526003602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b5460d01c64ffffffffff1690565b9160019889870193614812611572865464ffffffffff9060281c1690565b86821611614b1e578289939261488d6148929361483d886147c1896000526003602052604060002090565b907fff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff7effffffffff000000000000000000000000000000000000000000000000000083549260d01b169116179055565b614641565b9e8c8160058a01805492508215600014614ae5575087167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0187169790505b88886148e2825460ff9060a81c1690565b614a90575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff97947fa1b81c80064b5642431267c6e5e4aed96d5b588d231eeb1d3d0783fe2c59b2ff9488946149b7600286614949614a6b9d614a5d986115729e9a6152f2565b016149728561488d835479ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffffffff0000000000000000000000000000000000000000000000000000825416179055565b6149fa6149d2336147c1886000526003602052604060002090565b6149728561488d835479ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b5193845233602085015273ffffffffffffffffffffffffffffffffffffffff909416604084015264ffffffffff909316606083015279ffffffffffffffffffffffffffffffffffffffffffffffffffff92909216608082015290819060a0820190565b0390a15464ffffffffff1690565b0191161015614a80575b50500192919061467b565b614a89916142c8565b3880614a75565b505090919250614aa8611572865464ffffffffff1690565b8688161015614abc579088929188886148e7565b600490517f34b3fb3b000000000000000000000000000000000000000000000000000000008152fd5b613235610c92917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614b189501906139db565b966148d1565b600487517fd085f161000000000000000000000000000000000000000000000000000000008152fd5b600486517f3492aef6000000000000000000000000000000000000000000000000000000008152fd5b60046040517fb68c78dc000000000000000000000000000000000000000000000000000000008152fd5b99614bc0600183015473ffffffffffffffffffffffffffffffffffffffff9060501c1690565b73ffffffffffffffffffffffffffffffffffffffff808d16911603156146ef5760046040517ff5993428000000000000000000000000000000000000000000000000000000008152fd5b614c13906135fe565b966146b1565b73ffffffffffffffffffffffffffffffffffffffff818116614c40575050611b5090614fa0565b7f00000000000000000000000000000000000000000000000000000000000000001691823b156102d2576040517fda3e8ce400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301523360248301523060448301526064820152906000908290608490829084905af18015610cdd57614cdb5750565b611b509061061d565b9160009260009260005b818110614cfa57505050565b614d08818385989798613aa0565b3594614d1e866000526002602052604060002090565b966006614d30895460ff9060a01c1690565b614d3981611d4a565b1061456e57614d56336147c1896000526003602052604060002090565b9788549279ffffffffffffffffffffffffffffffffffffffffffffffffffff84169360f81c8015614f23575b61255c5784614e65575091614e27817fc0b7cf12926534c2d86d0abb17d620a3a06e07a84b3b8156a4b21189222b16d29361488d614ddd6001809998015473ffffffffffffffffffffffffffffffffffffffff9060501c1690565b9c7f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff825416179055565b60408051998a523360208b015279ffffffffffffffffffffffffffffffffffffffffffffffffffff929092169189019190915296606090a101614cee565b98919392906124b06001614e9292015473ffffffffffffffffffffffffffffffffffffffff9060501c1690565b73ffffffffffffffffffffffffffffffffffffffff8a16036121c657614e278160019561488d7fc0b7cf12926534c2d86d0abb17d620a3a06e07a84b3b8156a4b21189222b16d2957f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff825416179055565b508315614d82565b73ffffffffffffffffffffffffffffffffffffffff163303614f4957565b60046040517f48f5c3ed000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff1660005260046020526001604060002054036121c657565b34811115614fd25760046040517f8ffff980000000000000000000000000000000000000000000000000000000008152fd5b803411614fdc5750565b611b50905a903403337f00000000000000000000000000000000000000000000000000000000000000006158e3565b64ffffffffff9061501b816145fc565b5460b01c16421061502857565b60046040517ff9ad93f5000000000000000000000000000000000000000000000000000000008152fd5b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167404000000000000000000000000000000000000000017905560408051918252600460208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167406000000000000000000000000000000000000000017905560408051918252600660208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167407000000000000000000000000000000000000000017905560408051918252600760208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167405000000000000000000000000000000000000000017905560408051918252600560208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167402000000000000000000000000000000000000000017905560408051918252600260208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167403000000000000000000000000000000000000000017905560408051918252600360208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b6040519291906005016153048461066e565b64ffffffffff809216845273ffffffffffffffffffffffffffffffffffffffff60208501931683528054680100000000000000008110156106315761534e916001820181556139db565b9390936153a85751835492517fffffffffffffff000000000000000000000000000000000000000000000000009390931691161760289190911b78ffffffffffffffffffffffffffffffffffffffff000000000016179055565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b8115613b89570690565b60019391848101808211612bde578015613b8957820694600886811c9260ff908380838b161b945b61542f575b505050506154299061542083876139c7565b511791856139c7565b52929190565b9380615443878b9c9599969c9894986139c7565b5116156154915750505081906040516154688161168260208201948560209181520190565b5190209361547e61547885613791565b866153d7565b9788811c9483838b161b94909193615409565b9382995083965081955061540e565b805191821561553a576000925b808410615509575082151591826154eb575b5050156135f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b6155009192506154fa84613899565b906139c7565b511438806154bf565b6155138185615a17565b90818361552082876139c7565b51111561552e5750506154ad565b600101945090506154ad565b505050600090565b907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff81163303615618575081600052600560205260406000205460ff811661559e57505050565b69ffffffffffffffffffff9060081c1680600052600260205260406000209060026155ce835460ff9060a01c1690565b6155d781611d4a565b146155e3575b50505050565b6001926155f66155fb9261560d94615282565b6139ba565b51926000526005602052604060002090565b0155388080806155dd565b6040517f1cf993f400000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff919091166024820152604490fd5b73ffffffffffffffffffffffffffffffffffffffff60005416330361568857565b60046040517f30cd7471000000000000000000000000000000000000000000000000000000008152fd5b60ff60015460b01c166156c157565b60046040517f1309a563000000000000000000000000000000000000000000000000000000008152fd5b3d15615744573d9067ffffffffffffffff8211610631576040519161573860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846106a7565b82523d6000602084013e565b606090565b91909392823b1561426657600094859485926040519260208401957ff242432a00000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff80921660248601521660448401526064830152608482015260a060a48201528160c482015260c481526157ce8161068a565b51925af16157da6156eb565b50156157e257565b60046040517f02f8f11e000000000000000000000000000000000000000000000000000000008152fd5b908160209103126102d257516135f1816104e6565b909192813b15614266576040519260208401947f23b872dd00000000000000000000000000000000000000000000000000000000865273ffffffffffffffffffffffffffffffffffffffff809216602486015216604484015260648301526064825260a082019282841067ffffffffffffffff851117610631576000809493819460405251925af16158b16156eb565b50156158b957565b60046040517fe0f5c508000000000000000000000000000000000000000000000000000000008152fd5b6158f882849395600080809781948294f11590565b6159025750505050565b73ffffffffffffffffffffffffffffffffffffffff16803b15615a1357604051937fd0e30db0000000000000000000000000000000000000000000000000000000008552838560048186865af1938415610cdd576159bb95602095615a00575b506040518096819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af18015610cdd576159d2575b8080806155dd565b6159f29060203d81116159f9575b6159ea81836106a7565b81019061580c565b50386159ca565b503d6159e0565b80610cd1615a0d9261061d565b38615962565b8280fd5b90808216911860011c8101809111612bde579056fea2646970667358221220bf93bd80b91da28dc011ba8a77ed5bfbd550a6ba0168ccc0f3dc76a965b0552364736f6c634300081400330000000000000000000000007b79995e5f793a07bc00c21412e50ecae098e7f9474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c00000000000000000000000000000000000000000000000000000000000004620000000000000000000000008103b0a8a00be2ddc778e6e7eaa21791cd364625000000000000000000000000f332533bf5d0ac462dc8511067a8122b4dce2b5700000000000000000000000050f0787ed7c9091abca1d667fdbccd85ea68c38c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b43b6c4601facf70fe17d057b3912bde0206cfb
Deployed Bytecode
0x6080604052600436101561001257600080fd5b60003560e01c8063030932bb146102c25780630d64d5a6146102bd5780631e9f1a82146102b85780631fe543e3146102b3578063208b3d34146102ae57806323452b9c146102a95780632a6a4eb8146102a45780632bb5a9e61461029f57806331fe74a91461029a57806336566f061461029557806339a2eb2a146102905780633b6cb153146102545780633e5675391461028b57806340e58ee51461028657806349890e15146102815780634d2c53cd1461027c578063534fff0e146102775780635a195d19146102725780635b6ac0111461026d5780635c975abb146102685780635cb6dfff146102635780635d4bc0ce1461025e57806364df049e1461025957806365d697f7146102545780636b1426a41461024f5780636b7e4e3f1461024a5780636f30d7b5146102455780637200b829146102405780637762df251461023b5780637b154140146102365780638da5cb5b1461023157806398753c461461022c578063a17be15414610227578063a72844ba14610222578063ac68a7481461021d578063c0b6f56114610218578063cc30779514610213578063d1d06b2e1461020e578063d3549ebd14610209578063e521cb9214610204578063ea9bf39d146101ff578063f23a6e61146101fa5763f54a6f83146101f557600080fd5b613546565b6134a1565b613467565b613360565b6132aa565b613149565b613032565b612e7d565b612be3565b612aec565b612a37565b6129f8565b6129a6565b61277f565b61272d565b6125be565b61223d565b612073565b611f94565b611734565b611ebe565b611df3565b611cdb565b611c97565b611bbb565b611b52565b6119e2565b61199a565b61195f565b61186c565b61176e565b61148a565b611344565b6112a8565b6111f0565b6110e9565b610fcd565b6107aa565b610700565b6104f0565b61036b565b6102d7565b60009103126102d257565b600080fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060405167ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000462168152f35b9181601f840112156102d25782359167ffffffffffffffff83116102d2576020808501948460071b0101116102d257565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760043567ffffffffffffffff81116102d2576103b590369060040161033a565b600260ff60015460a81c161461048b5761043a79ffffffffffffffffffffffffffffffffffffffffffffffffffff916104449361042d75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6104356156b2565b61466f565b9190911690614c19565b61048975010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b005b60046040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b9181601f840112156102d25782359167ffffffffffffffff83116102d2576020808501948460051b0101116102d257565b801515036102d257565b346102d2576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600490813567ffffffffffffffff81116102d25761054190369084016104b5565b916024359361054f856104e6565b610557615667565b60005b8481106105985750507fcb2fef16ee9f3ea43e174daf88ffcdc8beee4ad884c7570093d998d71058698793610593915193849384613b8e565b0390a1005b600190600087156105e6575060ff825b73ffffffffffffffffffffffffffffffffffffffff6105c8848a8a613aa0565b356105d281611b27565b16600052846020521684600020550161055a565b60ff906105a8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff811161063157604052565b6105ee565b6080810190811067ffffffffffffffff82111761063157604052565b60e0810190811067ffffffffffffffff82111761063157604052565b6040810190811067ffffffffffffffff82111761063157604052565b610100810190811067ffffffffffffffff82111761063157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761063157604052565b67ffffffffffffffff81116106315760051b60200190565b346102d25760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760243567ffffffffffffffff81116102d257366023820112156102d25780600401359061075b826106e8565b9061076960405192836106a7565b82825260209260248484019160051b830101913683116102d257602401905b82821061079b5761048984600435615542565b81358152908401908401610788565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6020813601126102d25767ffffffffffffffff600435116102d25761010090600435360301126102d257600260ff60015460a81c161461048b5761084a75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6108526156b2565b6108606004356004016135e7565b64ffffffffff81168062015180420111908115610fbe575b50610f945761088b6084600435016135f4565b600654908160f01c61ffff821603610f6a576108ab60a4600435016135fe565b9073ffffffffffffffffffffffffffffffffffffffff8216610f5c575b6108dc60c460043501600435600401613608565b80939150158015610f52575b610f285769ffffffffffffffffffff9461093a866001818816011669ffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffff000000000000000000006006541617600655565b85600181871601166000526002602052604060002093600485016000526020600020906000906000926000906000915b838310610b0057505050600487015561098290614fa0565b64ffffffffff6109966044600435016135e7565b911664ffffffffff82168111908115610af5575b50610acb576001936109d76109c960e4600435016004356004016137f2565b90848b89818d160116613ce8565b740100000000000000000000000000000000000000006109fb602460043501613846565b610a096064600435016135e7565b9460b01b9060a81b33171717865560f01b9260501b9160281b1717179101557fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65160405180610a6c8560018187160116826020600191939293604081019481520152565b0390a1610ab475010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6040519082166001019091168152602090f35b0390f35b60046040517f804a1e02000000000000000000000000000000000000000000000000000000008152fd5b60c8915011386109aa565b610b29610b2484610b1e60c460049a979a3501600435600401613608565b9061368b565b6136ae565b9560ff610b3a606089015160ff1690565b921660ff831610610efe57610b4e87613fb0565b604087015196610b5d88612f7e565b805164ffffffffff1694610b88608083015173ffffffffffffffffffffffffffffffffffffffff1690565b60c060a084015193015192610b9c8b612f7e565b8a610ce25773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008b43b6c4601facf70fe17d057b3912bde0206cfb163b156102d2576040517fcda20f0b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83166004820152336024820152306044820152606481018290529560008780608481015b03818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008b43b6c4601facf70fe17d057b3912bde0206cfb165af1968715610cdd5789600295610c979260019a610cc4575b505b6137d9565b9b8c829a600389028b019560601b9360581b9260501b9160281b171717178255858201550155019161096a565b80610cd1610cd79261061d565b806102c7565b38610c90565b613785565b610ceb8b612f7e565b60038b03610da557610d0464ffffffffff891685613772565b9573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008b43b6c4601facf70fe17d057b3912bde0206cfb163b156102d2576040517fda3e8ce400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416600482015233602482015230604482015260648101979097526000878060848101610c3d565b9198610db58b9692949596612f7e565b60028b03610deb5791600195949391610c9789610de5600296610ddf64ffffffffff84168a613772565b9061379f565b9c6137d9565b9891610e0364ffffffffff8997969593971685613772565b9573ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008b43b6c4601facf70fe17d057b3912bde0206cfb163b156102d2576040517fe62edc3500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201523360248201523060448201526064810183905260848101979097526000878060a4810103818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008b43b6c4601facf70fe17d057b3912bde0206cfb165af1968715610cdd5789600295610c979260019a610cc457506137d9565b60046040517f5f12e2ee000000000000000000000000000000000000000000000000000000008152fd5b60046040517fab13062d000000000000000000000000000000000000000000000000000000008152fd5b5060c883116108e8565b610f6582614f73565b6108c8565b60046040517f4929acd7000000000000000000000000000000000000000000000000000000008152fd5b60046040517fce8f4bfd000000000000000000000000000000000000000000000000000000008152fd5b905062093a8042011038610878565b346102d2576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110da57611005615667565b60015460ff8160a01c1660038110156110d55780156110ab578061102a6001926111e6565b14611080575b507fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff600154166001557f8eca980489e87f7dba4f26917aa4bfc906eb3f2b4f7b4b9fd0ff2b8bb3e21ae38180a180f35b7fffffffffffffffffffffffff00000000000000000000000000000000000000001660015538611030565b60046040517fccf69db7000000000000000000000000000000000000000000000000000000008152fd5b6111b7565b80fd5b61ffff8116036102d257565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435611124816110dd565b61112c615667565b61ffff8116906109c48211610f6a577fede4aee4284b8033b84c1aadcc51b229a4e46e6b42ab40092e237f07508b4626916020917dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffff0000000000000000000000000000000000000000000000000000000000006006549260f01b16911617600655604051908152a1005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600311156110d557565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760ff60015460a01c1660405160038210156110d5576020918152f35b60208082019080835283518092528060408094019401926000905b83821061126557505050505090565b8451805164ffffffffff16875283015179ffffffffffffffffffffffffffffffffffffffffffffffffffff16868401529485019493820193600190910190611256565b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760043560005260028152600360406000200180546112f5816106e8565b9161130360405193846106a7565b8183526000908152838120938084015b8383106113285760405180610ac7878261123b565b600182819261133689613bfc565b815201960192019194611313565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25761137b615667565b60015460ff8160b01c16600014611417575060015460ff8160b01c16156113ed577fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff166001557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b60046040517f6cd60201000000000000000000000000000000000000000000000000000000008152fd5b7fffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffff760100000000000000000000000000000000000000000000916114596156b2565b16176001557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a1005b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576114d86114d36004356000526005602052604060002090565b613850565b6114e96114e58251151590565b1590565b61170a576115156115068383015169ffffffffffffffffffff1690565b69ffffffffffffffffffff1690565b9161152a836000526002602052604060002090565b9261153484614555565b61153e8185615052565b6004840191825493611583600561157c61157261156361155d8a613899565b896138c6565b505460281c64ffffffffff1690565b64ffffffffff1690565b97016138e2565b80519361158f8561396b565b9460005b8181106116da57506115a76115ad91613899565b866139c7565b51916115c36115be8460081c613791565b61396b565b976115cd8161396b565b9760005b8281106116b657505050604080940151916000526002855260068460002001978860005285600020936000935b8b851061160a578b8b55005b89946116346116508a6116228d978761162e976153e1565b908095929791996154a0565b896139c7565b51015173ffffffffffffffffffffffffffffffffffffffff1690565b61165e6001840180996154a0565b60a81b179060b01b179087015586516116ae816116828b8201948560209181520190565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826106a7565b5190206115fe565b806116c9611572611563600194866138c6565b6116d3828d6139c7565b52016115d1565b806116f96115726116ed600194886139c7565b515164ffffffffff1690565b611703828a6139c7565b5201611593565b60046040517f1b71a84c000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060405160c88152f35b346102d2576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110da576117a6615667565b60ff60015460a01c1660038110156110d557600203611842577fffffffffffffffffffffffff000000000000000000000000000000000000000081541681556118127fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b604051600081527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc90602090a180f35b60046040517f045c5122000000000000000000000000000000000000000000000000000000008152fd5b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600260ff60015460a81c161461048b57610444906118f775020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6118ff6156b2565b80600052600260205260406000206119168161500b565b805464ffffffffff61192c818360b01c166137ac565b16421061193a575b506150c2565b73ffffffffffffffffffffffffffffffffffffffff6119599116614f2b565b38611934565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760206040516109c48152f35b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602069ffffffffffffffffffff60065416604051908152f35b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600190600260ff835460a81c161461048b57611a6b75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b611a736156b2565b806000526002602052611a94604060002091611a8e83614598565b82615132565b6004810173ffffffffffffffffffffffffffffffffffffffff815492541660005b838110611b015761048975010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b80611b21611b108793866138c6565b508464ffffffffff825416916140de565b01611ab5565b73ffffffffffffffffffffffffffffffffffffffff8116036102d257565b3590611b5082611b27565b565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25773ffffffffffffffffffffffffffffffffffffffff600435611ba281611b27565b1660005260046020526020604060002054604051908152f35b346102d2576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126110da57611bf3615667565b60015460ff8160a01c1660038110156110d557611c6d577fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674020000000000000000000000000000000000000000176001557f3ff05a45e46337fa1cbf20996d2eeb927280bce099f37252bcca1040609604ec8180a180f35b60046040517f74ed79ae000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060ff60015460b01c166040519015158152f35b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576004356000526005602052606060406000206001815491015469ffffffffffffffffffff6040519260ff81161515845260081c1660208301526040820152f35b600811156110d557565b9893969a999597919a9290926101408a019b73ffffffffffffffffffffffffffffffffffffffff8095168b5260088110156110d5576101209979ffffffffffffffffffffffffffffffffffffffffffffffffffff9961ffff988d9560609460208801521515604087015264ffffffffff958694858094169101521660808d01521660a08b01521660c08901521660e08701521661010085015216910152565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600052600260205260406000208054610ac7611e636002600185015494015479ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b60405193849373ffffffffffffffffffffffffffffffffffffffff9164ffffffffff928160f01c93818360501c169381808560281c169416928160d81c928260b01c169160ff8160a81c169160ff8260a01c1691168b611d54565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602073ffffffffffffffffffffffffffffffffffffffff60065460501c16604051908152f35b60208082019080835283518092528060408094019401926000905b838210611f3d57505050505090565b8451805173ffffffffffffffffffffffffffffffffffffffff168752808401511515878501528082015160ff168783015260609081015164ffffffffff169087015260809095019493820193600190910190611f2e565b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576004356000526002815260409060068260002001805490611fe4826106e8565b92611ff1855194856106a7565b82845260009182528082208185015b84841061201457865180610ac78882611f13565b6001838192895161202481610636565b64ffffffffff865473ffffffffffffffffffffffffffffffffffffffff8116835260ff808260a01c161515868501528160a81c168d84015260b01c166060820152815201920193019290612000565b60407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25767ffffffffffffffff6004358181116102d2576120be9036906004016104b5565b916024359081116102d2576120d790369060040161033a565b929091600260ff60015460a81c161461048b5761214c916121449161213775020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b61213f6156b2565b614ce4565b93909261466f565b9073ffffffffffffffffffffffffffffffffffffffff809116908316036121c65779ffffffffffffffffffffffffffffffffffffffffffffffffffff9081169216828111156121a35791610444920390339061414d565b8281106121b3575b505050610444565b6121be920390614c19565b3880806121ab565b60046040517ff5993428000000000000000000000000000000000000000000000000000000008152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8201126102d2576004359067ffffffffffffffff82116102d257612239916004016104b5565b9091565b346102d25761224b366121f0565b90600260ff60015460a81c161461048b576122a175020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6122a96156b2565b6122b16139f3565b9160005b81811061231a5783602081015161230b5761048975010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b61231490614290565b80610444565b612325818385613a0c565b61233a81356000526002602052604060002090565b90600461234c835460ff9060a01c1690565b61235581611d4a565b036125b0575b6123686020820182613a4c565b9092600681015460005b8381106123bc57505050917f0d0ab4a45afc0276ae825be9eeb4552011b19aad5c92b4bb782f3ca0356fedc5916123b3600195946040519384933584613ab0565b0390a1016122b5565b6123c7818588613aa0565b3582811015612586576123dd90600685016139db565b509081549160ff8360a01c1661255c5760019261246861245f8e9361241873ffffffffffffffffffffffffffffffffffffffff809516614f2b565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000001781555460a81c60ff1690565b600488016138c6565b50908461247a835460ff9060501c1690565b61248381612f7e565b111561254a57815460601c906124c96124b0855173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b908216036124f057506002915001546124e760208d0191825161379f565b90525b01612372565b9061251d60029260208501948551612526579073ffffffffffffffffffffffffffffffffffffffff169052565b015490526124ea565b61252f81614290565b9073ffffffffffffffffffffffffffffffffffffffff169052565b506125579150339061405e565b6124ea565b60046040517f969bf728000000000000000000000000000000000000000000000000000000008152fd5b60046040517f63df8171000000000000000000000000000000000000000000000000000000008152fd5b6125b9826145b1565b61235b565b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600154600160ff8260a01c16612602816111e6565b036127035773ffffffffffffffffffffffffffffffffffffffff1633036126d957600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000163317905561267a7fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff60015416600155565b6126a77fffffffffffffffffffffffff000000000000000000000000000000000000000060015416600155565b6040513381527f3edd90e7770f06fafde38004653b33870066c33bfc923ff6102acd601f85dfbc908060208101610593565b60046040517fafdcfb92000000000000000000000000000000000000000000000000000000008152fd5b60046040517f5e4f2826000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b346102d25760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576004803560243590600260ff60015460a81c161461297d5761280b75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6128136156b2565b612827816000526002602052604060002090565b8054849060a01c60ff1661283a81611d4a565b0361296f575b6006810180548410156129465783612857916139db565b50805460ff8160a01c1661291d57917fd53b67ba94a5d6268d11caa5d2693557779404ed02fc9825d86d2894d29cb8fd956128b16128b89361241873ffffffffffffffffffffffffffffffffffffffff6128c09716614f2b565b91016138c6565b50339061405e565b60408051918252602082019290925290819081015b0390a161048975010000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b856040517f969bf728000000000000000000000000000000000000000000000000000000008152fd5b846040517f63df8171000000000000000000000000000000000000000000000000000000008152fd5b612978816145b1565b612840565b826040517f1bbee726000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060065460f01c604051908152f35b346102d25760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576060612aac602435612a7781611b27565b600435600052600360205260406000209073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b546040519079ffffffffffffffffffffffffffffffffffffffffffffffffffff8116825264ffffffffff8160d01c16602083015260f81c15156040820152f35b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600260ff60015460a81c161461048b57612b7375020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b612b7b6156b2565b806000526002602052604060002090612b93826145ca565b815460d81c620151808101809111612bde574210612bb457610444916150c2565b60046040517ff4c0ca6e000000000000000000000000000000000000000000000000000000008152fd5b613732565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600260ff60015460a81c161461048b577fa1f87f32d0f17fab0242ca800d736293de8988c14b27747e218cf13d5c249f5390612c8c75020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b612c946156b2565b80612ca9816000526002602052604060002090565b612cb2816145e3565b612d9a612cd3825473ffffffffffffffffffffffffffffffffffffffff1690565b9173ffffffffffffffffffffffffffffffffffffffff83163303612e51575b612de66002820191612ddf612d21845479ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b93612dc26001840191612d6f612d4b612d45612d3e865460f01c90565b61ffff1690565b89613af8565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff61271091160490565b97889779ffffffffffffffffffffffffffffffffffffffffffffffffffff998a9103169b8c966151a2565b7fffffffffffff00000000000000000000000000000000000000000000000000008154169055565b5460501c73ffffffffffffffffffffffffffffffffffffffff1690565b948561414d565b1680612e23575b50506040805191825279ffffffffffffffffffffffffffffffffffffffffffffffffffff909216602082015290819081016128d5565b600654612e4a929060501c73ffffffffffffffffffffffffffffffffffffffff169061414d565b3880612ded565b612e78612e7360005473ffffffffffffffffffffffffffffffffffffffff1690565b614f2b565b612cf2565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435612eb881611b27565b612ec0615667565b60015460ff8160a01c1660038110156110d557611c6d577fffffffffffffffffffffff0000000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff82161774010000000000000000000000000000000000000000176001557fb86c75c9bffca616b2d314cc914f7c3f1d174255b16b941c3f3ededee276d5ef90610593906040805133815273ffffffffffffffffffffffffffffffffffffffff909216602083015290918291820190565b600411156110d557565b60208082019080835283518092528060408094019401926000905b838210612fb257505050505090565b9091929394855164ffffffffff8082511683528582015116858301528281015160048110156110d5578284015260608181015160ff169083015260808082015173ffffffffffffffffffffffffffffffffffffffff169083015260a0808201519083015260c0908101519082015260e00194830193929160010190612fa3565b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600052600280825260409160048360002001805491613083836106e8565b93613090865195866106a7565b8385526000928352818320908286015b8585106130b457875180610ac78982612f88565b6003846001928a516130c581610652565b613129875464ffffffffff80821684528160281c16858401528d6130f360ff91828460501c16908601613bf0565b6131096060918360581c168286019060ff169052565b1c608083019073ffffffffffffffffffffffffffffffffffffffff169052565b8487015460a08201528587015460c08201528152019301940193916130a0565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600260ff60015460a81c161461048b576131d075020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b6131d86156b2565b6131ec816000526002602052604060002090565b6005810180549081156132805761324661157261324161323564ffffffffff9461322f61322961156360048b016132238154613899565b906138c6565b97613899565b906139db565b505464ffffffffff1690565b6137c3565b911611613280576104449161325a8261500b565b61327b612e73835473ffffffffffffffffffffffffffffffffffffffff1690565b6142c8565b60046040517f9358a89d000000000000000000000000000000000000000000000000000000008152fd5b346102d2576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257600435600052600281526040906132f6600583600020016138e2565b8251928284938401908085528351809252808386019401926000905b83821061331f5786860387f35b8451805164ffffffffff16875283015173ffffffffffffffffffffffffffffffffffffffff1686840152879650948501949382019360019190910190613312565b346102d25760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d25760043561339b81611b27565b6133a3615667565b73ffffffffffffffffffffffffffffffffffffffff811690811561343d577fc1b5345cce283376356748dc57f2dfa7120431d016fc7ca9ba641bc65f91411d916020917fffff0000000000000000000000000000000000000000ffffffffffffffffffff7dffffffffffffffffffffffffffffffffffffffff000000000000000000006006549260501b16911617600655604051908152a1005b60046040517f3106a0c7000000000000000000000000000000000000000000000000000000008152fd5b346102d25760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d257602060405160058152f35b346102d25760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102d2576134db600435611b27565b6134e6602435611b27565b60843567ffffffffffffffff8082116102d257366023830112156102d25781600401359081116102d257369101602401116102d2576040517ff23a6e61000000000000000000000000000000000000000000000000000000008152602090f35b346102d257613554366121f0565b600260ff60015460a81c161461048b576135cc79ffffffffffffffffffffffffffffffffffffffffffffffffffff916104449361213775020000000000000000000000000000000000000000007fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff6001541617600155565b9190911690339061414d565b64ffffffffff8116036102d257565b356135f1816135d8565b90565b356135f1816110dd565b356135f181611b27565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156102d2570180359067ffffffffffffffff82116102d2576020019160e08202360383136102d257565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919081101561369b5760e0020190565b61365c565b359060ff821682036102d257565b60e0813603126102d257604051906136c582610652565b80356136d0816135d8565b825260208101356136e0816135d8565b602083015260408101359060048210156102d25760c0916040840152613708606082016136a0565b606084015261371960808201611b45565b608084015260a081013560a0840152013560c082015290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9081156001838004141715612bde57565b81810292918115918404141715612bde57565b6040513d6000823e3d90fd5b9060018201809211612bde57565b91908201809211612bde57565b90610e1064ffffffffff80931601918211612bde57565b90600164ffffffffff80931601918211612bde57565b91909164ffffffffff80809416911601918211612bde57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156102d2570180359067ffffffffffffffff82116102d257602001918160061b360383136102d257565b356135f1816104e6565b906040516060810181811067ffffffffffffffff8211176106315760405260406001829469ffffffffffffffffffff815460ff81161515865260081c1660208501520154910152565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211612bde57565b805482101561369b576000526003602060002091020190600090565b9081546138ee816106e8565b926040936138fe855191826106a7565b828152809460208092019260005281600020906000935b85851061392457505050505050565b600184819284516139348161066e565b73ffffffffffffffffffffffffffffffffffffffff875464ffffffffff8116835260281c1683820152815201930194019391613915565b90613975826106e8565b61398260405191826106a7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06139b082946106e8565b0190602036910137565b80511561369b5760200190565b805182101561369b5760209160051b010190565b805482101561369b5760005260206000200190600090565b60405190613a008261066e565b60006020838281520152565b919081101561369b5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1813603018212156102d2570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156102d2570180359067ffffffffffffffff82116102d257602001918160051b360383136102d257565b919081101561369b5760051b0190565b908152604060208201528260408201527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116102d25760609260051b809284830137010190565b919079ffffffffffffffffffffffffffffffffffffffffffffffffffff8080941691169283820216928184041490151715612bde57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b9079ffffffffffffffffffffffffffffffffffffffffffffffffffff809116918215613b8957160490565b613b2f565b6040808252810183905292939291606083019060005b818110613bb957505060209150931515910152565b909160019073ffffffffffffffffffffffffffffffffffffffff8535613bde81611b27565b16815260209485019401929101613ba4565b60048210156110d55752565b90604051613c098161066e565b915464ffffffffff8116835260281c79ffffffffffffffffffffffffffffffffffffffffffffffffffff166020830152565b901561369b5790565b919081101561369b5760061b0190565b6040813603126102d257602060405191613c6d8361066e565b8035613c78816135d8565b8352013579ffffffffffffffffffffffffffffffffffffffffffffffffffff811681036102d257602082015290565b9064ffffffffff809116918215613b8957160690565b9079ffffffffffffffffffffffffffffffffffffffffffffffffffff809116918215613b8957160690565b83158015613fa6575b613f7c57613d07613d028585613c3b565b6135e7565b90600052602091600283526040600381600020019485600052846000209260005b888110613d39575050505050505055565b613d4c613d47828b86613c44565b613c54565b613d8188613d5f835164ffffffffff1690565b92015179ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b82613e055764ffffffffff613d968386613ca7565b1615801590613de1575b613db85790600192915b60281b178187015501613d28565b600486517f0b9752e0000000000000000000000000000000000000000000000000000000008152fd5b5079ffffffffffffffffffffffffffffffffffffffffffffffffffff811615613da0565b613e35613d477fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85018d88613c44565b613e6a613e5e8b83015179ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b915164ffffffffff1690565b64ffffffffff9182613e7c8c87613ca7565b161592831593613f4a575b8315613f3b575b8315613f11575b8315613ead575b505050613db8579060019291613daa565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff929350613f069181613edf613ee793891688613b5e565b951690613b5e565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b911611388080613e9c565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff82811690861611159350613e95565b82811686821611159350613e8e565b925079ffffffffffffffffffffffffffffffffffffffffffffffffffff613f7384871686613cbd565b16151592613e87565b60046040517f3116ff5d000000000000000000000000000000000000000000000000000000008152fd5b5060058411613cf1565b6040810151613fbe81612f7e565b613fc781612f7e565b80613ff95750600160c08201511490811591613fe5575b50610efe57565b5164ffffffffff1660011415905038613fde565b80614005600392612f7e565b14614030575b60c08101511590811561401f5750610efe57565b5164ffffffffff1615905038613fde565b614059614054608083015173ffffffffffffffffffffffffffffffffffffffff1690565b614f73565b61400b565b9081549060ff8260501c169160601c9161407781612f7e565b8061408d57506001611b50930154913090615821565b8061409c600192959495612f7e565b036140c15790611b5092916140b960026001840154930154613761565b923090615749565b90600201549081800460011482151715612bde57611b509261414d565b80549192606083901c9260501c60ff166140f781612f7e565b8061410f575050916001611b50930154913090615821565b8061411e600192969396612f7e565b03614139576140b9611b509460026001850154940154613772565b90611b5093600261414b920154613772565b915b90919073ffffffffffffffffffffffffffffffffffffffff8116614199575090611b50915a917f0000000000000000000000007b79995e5f793a07bc00c21412e50ecae098e7f96158e3565b803b15614266576040517fa9059cbb000000000000000000000000000000000000000000000000000000006020820190815273ffffffffffffffffffffffffffffffffffffffff90941660248201526044810192909252600092839283906142048160648101611682565b51925af16142106156eb565b901561423c57805180614221575050565b816020806114e593614236950101910161580c565b61423c57565b60046040517ff1568f95000000000000000000000000000000000000000000000000000000008152fd5b60046040517f09ee12d5000000000000000000000000000000000000000000000000000000008152fd5b611b5090602073ffffffffffffffffffffffffffffffffffffffff82511691015190339061414d565b908160209103126102d2575190565b90614321906142d78382615212565b80547affffffffffffffffffffffffffffffffffffffffffffffffffffff164260d81b7fffffffffff00000000000000000000000000000000000000000000000000000016179055565b6040517f5d3b1d300000000000000000000000000000000000000000000000000000000081527f474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c60048201527f000000000000000000000000000000000000000000000000000000000000046267ffffffffffffffff166024820152600360448201526207a12060648201526001608482015260208160a48160007f0000000000000000000000008103b0a8a00be2ddc778e6e7eaa21791cd36462573ffffffffffffffffffffffffffffffffffffffff165af1908115610cdd57600091614527575b5061442361441c826000526005602052604060002090565b5460ff1690565b6144fd577f3d94fecedaa4f90b8bd459797adb95f5bb11426025c5541390d9ccc1ad1b60a19161448b614460836000526005602052604060002090565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b6144eb69ffffffffffffffffffff82166144af846000526005602052604060002090565b907fffffffffffffffffffffffffffffffffffffffffff00000000000000000000ff6affffffffffffffffffff0083549260081b169116179055565b604080519182526020820192909252a1565b60046040517ff9012132000000000000000000000000000000000000000000000000000000008152fd5b614548915060203d811161454e575b61454081836106a7565b8101906142b9565b38614404565b503d614536565b5460a01c60ff1660088110156110d55760030361456e57565b60046040517ff525e320000000000000000000000000000000000000000000000000000000008152fd5b5460a01c60ff1660088110156110d55760060361456e57565b5460a01c60ff1660088110156110d55760050361456e57565b5460a01c60ff1660088110156110d55760020361456e57565b5460a01c60ff1660088110156110d55760040361456e57565b5460a01c60ff1660088110156110d55760010361456e57565b919081101561369b5760071b0190565b91909164ffffffffff80809416911602918216918203612bde57565b91909179ffffffffffffffffffffffffffffffffffffffffffffffffffff80809416911601918211612bde57565b90916000926000928380925b8084106146885750505050565b9091929594614698878386614615565b96606088016146a96124b0826135fe565b614c0a575033965b8835906146c8826000526002602052604060002090565b9083614b9a5750600181015460501c73ffffffffffffffffffffffffffffffffffffffff16995b6020808201356003840180548210156125865761470b856145fc565b84546147209060b01c64ffffffffff16611572565b421015614b705761473a91614734916139db565b50613bfc565b9a61474860408094016135e7565b64ffffffffff92838216918215614b47576147a192918f61479c9261477561477a925164ffffffffff1690565b614625565b9f015179ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b613af8565b906147f48d610c926147e6846147c18b6000526003602052604060002090565b9073ffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b5460d01c64ffffffffff1690565b9160019889870193614812611572865464ffffffffff9060281c1690565b86821611614b1e578289939261488d6148929361483d886147c1896000526003602052604060002090565b907fff0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff7effffffffff000000000000000000000000000000000000000000000000000083549260d01b169116179055565b614641565b9e8c8160058a01805492508215600014614ae5575087167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0187169790505b88886148e2825460ff9060a81c1690565b614a90575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff97947fa1b81c80064b5642431267c6e5e4aed96d5b588d231eeb1d3d0783fe2c59b2ff9488946149b7600286614949614a6b9d614a5d986115729e9a6152f2565b016149728561488d835479ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffffffff0000000000000000000000000000000000000000000000000000825416179055565b6149fa6149d2336147c1886000526003602052604060002090565b6149728561488d835479ffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b5193845233602085015273ffffffffffffffffffffffffffffffffffffffff909416604084015264ffffffffff909316606083015279ffffffffffffffffffffffffffffffffffffffffffffffffffff92909216608082015290819060a0820190565b0390a15464ffffffffff1690565b0191161015614a80575b50500192919061467b565b614a89916142c8565b3880614a75565b505090919250614aa8611572865464ffffffffff1690565b8688161015614abc579088929188886148e7565b600490517f34b3fb3b000000000000000000000000000000000000000000000000000000008152fd5b613235610c92917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff614b189501906139db565b966148d1565b600487517fd085f161000000000000000000000000000000000000000000000000000000008152fd5b600486517f3492aef6000000000000000000000000000000000000000000000000000000008152fd5b60046040517fb68c78dc000000000000000000000000000000000000000000000000000000008152fd5b99614bc0600183015473ffffffffffffffffffffffffffffffffffffffff9060501c1690565b73ffffffffffffffffffffffffffffffffffffffff808d16911603156146ef5760046040517ff5993428000000000000000000000000000000000000000000000000000000008152fd5b614c13906135fe565b966146b1565b73ffffffffffffffffffffffffffffffffffffffff818116614c40575050611b5090614fa0565b7f0000000000000000000000008b43b6c4601facf70fe17d057b3912bde0206cfb1691823b156102d2576040517fda3e8ce400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9290921660048301523360248301523060448301526064820152906000908290608490829084905af18015610cdd57614cdb5750565b611b509061061d565b9160009260009260005b818110614cfa57505050565b614d08818385989798613aa0565b3594614d1e866000526002602052604060002090565b966006614d30895460ff9060a01c1690565b614d3981611d4a565b1061456e57614d56336147c1896000526003602052604060002090565b9788549279ffffffffffffffffffffffffffffffffffffffffffffffffffff84169360f81c8015614f23575b61255c5784614e65575091614e27817fc0b7cf12926534c2d86d0abb17d620a3a06e07a84b3b8156a4b21189222b16d29361488d614ddd6001809998015473ffffffffffffffffffffffffffffffffffffffff9060501c1690565b9c7f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff825416179055565b60408051998a523360208b015279ffffffffffffffffffffffffffffffffffffffffffffffffffff929092169189019190915296606090a101614cee565b98919392906124b06001614e9292015473ffffffffffffffffffffffffffffffffffffffff9060501c1690565b73ffffffffffffffffffffffffffffffffffffffff8a16036121c657614e278160019561488d7fc0b7cf12926534c2d86d0abb17d620a3a06e07a84b3b8156a4b21189222b16d2957f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff825416179055565b508315614d82565b73ffffffffffffffffffffffffffffffffffffffff163303614f4957565b60046040517f48f5c3ed000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff1660005260046020526001604060002054036121c657565b34811115614fd25760046040517f8ffff980000000000000000000000000000000000000000000000000000000008152fd5b803411614fdc5750565b611b50905a903403337f0000000000000000000000007b79995e5f793a07bc00c21412e50ecae098e7f96158e3565b64ffffffffff9061501b816145fc565b5460b01c16421061502857565b60046040517ff9ad93f5000000000000000000000000000000000000000000000000000000008152fd5b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167404000000000000000000000000000000000000000017905560408051918252600460208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167406000000000000000000000000000000000000000017905560408051918252600660208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167407000000000000000000000000000000000000000017905560408051918252600760208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167405000000000000000000000000000000000000000017905560408051918252600560208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167402000000000000000000000000000000000000000017905560408051918252600260208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b80547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff167403000000000000000000000000000000000000000017905560408051918252600360208301527fc1191e7178b58ad510709587719f39ec315fa79e81ee7ba5c5ef3c894e94a65191a1565b6040519291906005016153048461066e565b64ffffffffff809216845273ffffffffffffffffffffffffffffffffffffffff60208501931683528054680100000000000000008110156106315761534e916001820181556139db565b9390936153a85751835492517fffffffffffffff000000000000000000000000000000000000000000000000009390931691161760289190911b78ffffffffffffffffffffffffffffffffffffffff000000000016179055565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600060045260246000fd5b8115613b89570690565b60019391848101808211612bde578015613b8957820694600886811c9260ff908380838b161b945b61542f575b505050506154299061542083876139c7565b511791856139c7565b52929190565b9380615443878b9c9599969c9894986139c7565b5116156154915750505081906040516154688161168260208201948560209181520190565b5190209361547e61547885613791565b866153d7565b9788811c9483838b161b94909193615409565b9382995083965081955061540e565b805191821561553a576000925b808410615509575082151591826154eb575b5050156135f1577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b6155009192506154fa84613899565b906139c7565b511438806154bf565b6155138185615a17565b90818361552082876139c7565b51111561552e5750506154ad565b600101945090506154ad565b505050600090565b907f0000000000000000000000008103b0a8a00be2ddc778e6e7eaa21791cd36462573ffffffffffffffffffffffffffffffffffffffff81163303615618575081600052600560205260406000205460ff811661559e57505050565b69ffffffffffffffffffff9060081c1680600052600260205260406000209060026155ce835460ff9060a01c1690565b6155d781611d4a565b146155e3575b50505050565b6001926155f66155fb9261560d94615282565b6139ba565b51926000526005602052604060002090565b0155388080806155dd565b6040517f1cf993f400000000000000000000000000000000000000000000000000000000815233600482015273ffffffffffffffffffffffffffffffffffffffff919091166024820152604490fd5b73ffffffffffffffffffffffffffffffffffffffff60005416330361568857565b60046040517f30cd7471000000000000000000000000000000000000000000000000000000008152fd5b60ff60015460b01c166156c157565b60046040517f1309a563000000000000000000000000000000000000000000000000000000008152fd5b3d15615744573d9067ffffffffffffffff8211610631576040519161573860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846106a7565b82523d6000602084013e565b606090565b91909392823b1561426657600094859485926040519260208401957ff242432a00000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff80921660248601521660448401526064830152608482015260a060a48201528160c482015260c481526157ce8161068a565b51925af16157da6156eb565b50156157e257565b60046040517f02f8f11e000000000000000000000000000000000000000000000000000000008152fd5b908160209103126102d257516135f1816104e6565b909192813b15614266576040519260208401947f23b872dd00000000000000000000000000000000000000000000000000000000865273ffffffffffffffffffffffffffffffffffffffff809216602486015216604484015260648301526064825260a082019282841067ffffffffffffffff851117610631576000809493819460405251925af16158b16156eb565b50156158b957565b60046040517fe0f5c508000000000000000000000000000000000000000000000000000000008152fd5b6158f882849395600080809781948294f11590565b6159025750505050565b73ffffffffffffffffffffffffffffffffffffffff16803b15615a1357604051937fd0e30db0000000000000000000000000000000000000000000000000000000008552838560048186865af1938415610cdd576159bb95602095615a00575b506040518096819582947fa9059cbb000000000000000000000000000000000000000000000000000000008452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af18015610cdd576159d2575b8080806155dd565b6159f29060203d81116159f9575b6159ea81836106a7565b81019061580c565b50386159ca565b503d6159e0565b80610cd1615a0d9261061d565b38615962565b8280fd5b90808216911860011c8101809111612bde579056fea2646970667358221220bf93bd80b91da28dc011ba8a77ed5bfbd550a6ba0168ccc0f3dc76a965b0552364736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000007b79995e5f793a07bc00c21412e50ecae098e7f9474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c00000000000000000000000000000000000000000000000000000000000004620000000000000000000000008103b0a8a00be2ddc778e6e7eaa21791cd364625000000000000000000000000f332533bf5d0ac462dc8511067a8122b4dce2b5700000000000000000000000050f0787ed7c9091abca1d667fdbccd85ea68c38c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000008b43b6c4601facf70fe17d057b3912bde0206cfb
-----Decoded View---------------
Arg [0] : _weth (address): 0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9
Arg [1] : _keyHash (bytes32): 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c
Arg [2] : _subscriptionId (uint64): 1122
Arg [3] : _vrfCoordinator (address): 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625
Arg [4] : _owner (address): 0xF332533bF5d0aC462DC8511067A8122b4DcE2B57
Arg [5] : _protocolFeeRecipient (address): 0x50F0787Ed7C9091aBCa1D667fDBCcd85EA68C38C
Arg [6] : _protocolFeeBp (uint16): 0
Arg [7] : _transferManager (address): 0x8B43b6C4601FaCF70Fe17D057b3912Bde0206CFB
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000007b79995e5f793a07bc00c21412e50ecae098e7f9
Arg [1] : 474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000462
Arg [3] : 0000000000000000000000008103b0a8a00be2ddc778e6e7eaa21791cd364625
Arg [4] : 000000000000000000000000f332533bf5d0ac462dc8511067a8122b4dce2b57
Arg [5] : 00000000000000000000000050f0787ed7c9091abca1d667fdbccd85ea68c38c
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000008b43b6c4601facf70fe17d057b3912bde0206cfb
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.